No OneTemporary

File Metadata

Created
Sat, Jun 8, 5:57 PM
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d454821caae..9201d209cdf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,1166 +1,1166 @@
project(calligra)
message(STATUS "Using CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
cmake_policy(SET CMP0002 OLD)
#cmake_policy CMP0017 was introduced in version 2.8.4
-if(${CMAKE_VERSION} VERSION_GREATER 2.8.3)
+if (NOT ${CMAKE_VERSION} VERSION_LESS 2.8.3)
cmake_policy(SET CMP0017 NEW)
-endif()
+endif ()
-if(${CMAKE_VERSION} VERSION_GREATER 2.8.12)
+if (NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
-endif()
+endif ()
-if(${CMAKE_VERSION} VERSION_GREATER 3.0.0)
+if (NOT ${CMAKE_VERSION} VERSION_LESS 3.0.0)
cmake_policy(SET CMP0026 OLD)
cmake_policy(SET CMP0046 OLD)
-endif()
+endif ()
-if(${CMAKE_VERSION} VERSION_GREATER 3.3.0)
+if(NOT ${CMAKE_VERSION} VERSION_LESS 3.3.0)
cmake_policy(SET CMP0063 OLD)
endif()
# QT5TODO: remove KDE4_BUILD_TESTS once all kde4_add_unit_test have been converted
# transitional forward compatibility:
# BUILD_TESTING is cmake standard, KDE4_BUILD_TESTS not used by ECM/KF5, but only
# macros in cmake/transitional. Just, Macros from cmake/transitional,
# incl. kde4_add_unit_test, are only picked up if no macros from kdelibs4 are installed,
# because that transitional path is appended. Prepending instead might possibly unwantedly
# mask ECM/KF5 macros. Not tested.
# So have BUILD_TESTING define KDE4_BUILD_TESTS.
if (BUILD_TESTING)
set(KDE4_BUILD_TESTS TRUE)
else()
set(KDE4_BUILD_TESTS FALSE)
endif()
######################
#######################
## Constants defines ##
#######################
######################
# define common versions of Calligra applications, used to generate calligraversion.h
# update these version for every release:
set(CALLIGRA_VERSION_STRING "3.0 Alpha")
set(CALLIGRA_STABLE_VERSION_MAJOR 3) # 3 for 3.x, 4 for 4.x, etc.
set(CALLIGRA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc.
set(CALLIGRA_VERSION_RELEASE 89) # 89 for Alpha, increase for next test releases, set 0 for first Stable, etc.
set(CALLIGRA_ALPHA 1) # uncomment only for Alpha
#set(CALLIGRA_BETA 1) # uncomment only for Beta
#set(CALLIGRA_RC 1) # uncomment only for RC
set(CALLIGRA_YEAR 2015) # update every year
if(NOT DEFINED CALLIGRA_ALPHA AND NOT DEFINED CALLIGRA_BETA AND NOT DEFINED CALLIGRA_RC)
set(CALLIGRA_STABLE 1) # do not edit
endif()
message(STATUS "Calligra version: ${CALLIGRA_VERSION_STRING}")
# Define the generic version of the Calligra libraries here
# This makes it easy to advance it when the next Calligra release comes.
# 14 was the last GENERIC_CALLIGRA_LIB_VERSION_MAJOR of the previous Calligra series
# (2.x) so we're starting with 15 in 3.x series.
if(CALLIGRA_STABLE_VERSION_MAJOR EQUAL 3)
math(EXPR GENERIC_CALLIGRA_LIB_VERSION_MAJOR "${CALLIGRA_STABLE_VERSION_MINOR} + 15")
else()
# let's make sure we won't forget to update the "15"
message(FATAL_ERROR "Reminder: please update offset == 15 used to compute GENERIC_CALLIGRA_LIB_VERSION_MAJOR to something bigger")
endif()
set(GENERIC_CALLIGRA_LIB_VERSION "${GENERIC_CALLIGRA_LIB_VERSION_MAJOR}.0.0")
set(GENERIC_CALLIGRA_LIB_SOVERSION "${GENERIC_CALLIGRA_LIB_VERSION_MAJOR}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/modules")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/transitional")
message("Module path:" ${CMAKE_MODULE_PATH})
# fetch git revision for the current build
set(CALLIGRA_GIT_SHA1_STRING "")
set(CALLIGRA_GIT_BRANCH_STRING "")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
get_git_branch(GIT_BRANCH)
if(GIT_SHA1 AND GIT_BRANCH)
string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1)
set(CALLIGRA_GIT_SHA1_STRING ${GIT_SHA1})
set(CALLIGRA_GIT_BRANCH_STRING ${GIT_BRANCH})
endif()
if(NOT DEFINED RELEASE_BUILD)
# estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER)
set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel")
list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX)
if (INDEX EQUAL -1)
set(RELEASE_BUILD FALSE)
else()
set(RELEASE_BUILD TRUE)
endif()
endif()
message(STATUS "Release build: ${RELEASE_BUILD}")
############
#############
## Options ##
#############
############
option(USEOPENGL "Allow the use of OpenGL for Krita" ON)
if (WIN32)
option(USE_BREAKPAD "Build the crash handler for Krita (only on windows)" OFF)
endif ()
option(GHNS "support Get Hot New Stuff" OFF)
# TODO: orthogonal setting, results in minimal features, yet needs to be defined
# option(TINY "compile a tiny Calligra" OFF)
option(PACKAGERS_BUILD "Build support of multiple CPU architectures in one binary. Should be used by packagers only." ON)
# TODO: remove option and migration code below before 3.0 release
option(CREATIVEONLY "compile only Karbon and Krita" OFF)
#######################
########################
## Productset setting ##
########################
#######################
# For predefined productsets see the definitions in CalligraProducts.cmake and
# in the files in the folder cmake/productsets.
# Finding out the products & features to build is done in 5 steps:
# 1. have the user define the products/features wanted, by giving a productset
# 2. estimate all additional required products/features
# 3. estimate which of the products/features can be build by external deps
# 4. find which products/features have been temporarily disabled due to problems
# 5. estimate which of the products/features can be build by internal deps
# get the special macros
include(CalligraProductSetMacros)
include(MacroJPEG)
# get the definitions of products, features and product sets
include(CalligraProducts.cmake)
set(PRODUCTSET_DEFAULT "ALL")
# temporary migration support
if (CREATIVEONLY)
set(WARN_ABOUT_CREATIVEONLY TRUE)
set(PRODUCTSET_DEFAULT "CREATIVE")
endif ()
if(NOT PRODUCTSET)
set(PRODUCTSET ${PRODUCTSET_DEFAULT} CACHE STRING "Set of products/features to build" FORCE)
endif()
if (RELEASE_BUILD)
set(CALLIGRA_SHOULD_BUILD_STAGING FALSE)
else ()
set(CALLIGRA_SHOULD_BUILD_STAGING TRUE)
endif ()
# finally choose products/features to build
calligra_set_productset(${PRODUCTSET})
########################
#########################
## Look for KDE and Qt ##
#########################
########################
find_package(ECM 1.7.0 REQUIRED NOMODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(ECMOptionalAddSubdirectory)
include(ECMInstallIcons)
include(ECMAddAppIcon)
include(ECMSetupVersion)
include(ECMMarkNonGuiExecutable)
include(ECMGenerateHeaders)
include(GenerateExportHeader)
include(ECMMarkAsTest)
include(CMakePackageConfigHelpers)
include(WriteBasicConfigVersionFile)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings)
include(FeatureSummary)
find_package(KF5 5.7.0 REQUIRED COMPONENTS Archive Codecs Config CoreAddons
GuiAddons I18n ItemModels ItemViews
WidgetsAddons ThreadWeaver
Completion IconThemes Sonnet
Parts KIO
XmlGui Kross Wallet GlobalAccel
Emoticons ConfigWidgets KDELibs4Support
TextWidgets TextEditor KIO
OPTIONAL_COMPONENTS
NotifyConfig
Activities
KHtml
)
if(KF5_Activities_FOUND)
set(HAVE_KACTIVITIES TRUE)
endif()
find_package(Qt5 5.3.0 REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport
Script Svg Test Concurrent)
find_package(Qt5 5.3.0 COMPONENTS UiTools Sql WebKit WebKitWidgets DBus Declarative X11Extras)
set(QT_QTDBUS_FOUND ${Qt5DBus_FOUND})
set(QT_QTWEBKIT_FOUND ${Qt5WebKit_FOUND})
set(QT_QTDECLARATIVE_FOUND ${Qt5Declarative_FOUND})
set(QT_QTTEST_LIBRARY Qt5::Test)
if (USEOPENGL)
find_package(Qt5 5.3.0 REQUIRED OpenGL)
set(QT_QTOPENGL_FOUND ${Qt5OpenGL_FOUND})
endif ()
include (MacroLibrary)
include (MacroAdditionalCleanFiles)
include (MacroAddFileDependencies)
include (ECMInstallIcons)
if (GHNS)
find_package(Attica)
find_package(NewStuff)
macro_log_feature(LIBATTICA_FOUND "LibAttica" "Attica is used for Get Hot New Stuff." "https://projects.kde.org/projects/kdesupport/attica" FALSE "" "You need at least version 3.0 for uploading of resources to work.")
if (NOT LIBATTICA_FOUND)
set(GHNS FALSE)
else ()
message(STATUS "WARNING: You are compiling with Get Hot New Stuff enabled. Do not do that when building distribution packages. GHNS is unusable these days until someone starts maintaining it again.")
endif ()
endif ()
macro_ensure_out_of_source_build("Compiling Calligra inside the source directory is not possible. Please refer to the build instruction http://community.kde.org/Calligra/Building/Building_Calligra")
find_package(X11)
if(X11_FOUND)
find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS
X11Extras
)
set(HAVE_X11 TRUE)
add_definitions(-DHAVE_X11)
else()
set(HAVE_X11 FALSE)
endif()
# only with this definition will all the FOO_TEST_EXPORT macro do something
# TODO: check if this can be moved to only those places which make use of it,
# to reduce global compiler definitions that would trigger a recompile of
# everything on a change (like adding/removing tests to/from the build)
if(BUILD_TESTING)
add_definitions(-DCOMPILING_TESTS)
endif()
# overcome some platform incompatibilities
if(WIN32)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks)
add_definitions(-D_USE_MATH_DEFINES)
add_definitions(-DNOMINMAX)
set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib)
endif()
# would need more code changes before 4.8.0, e.g. with qPrintable()
if(NOT ${QTVERSION} VERSION_LESS 4.8.0)
# enable QStringBuilder enhancement
add_definitions(
-DQT_USE_FAST_CONCATENATION
-DQT_USE_FAST_OPERATOR_PLUS
)
endif()
# set custom calligra plugin installdir
set(CALLIGRA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/calligra)
# TEMPORARY: for initial Qt5/KF5 build porting phase deprecation warnings are only annoying noise
# remove once code porting phase starts, perhaps first locally in product subdirs
if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC)
add_definitions(-Wno-deprecated -Wno-deprecated-declarations)
endif ()
###########################
############################
## Required dependencies ##
############################
###########################
find_package(Perl REQUIRED)
find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED)
if (APPLE)
# this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242
include_directories(${PNG_INCLUDE_DIR})
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost REQUIRED COMPONENTS system) # for pigment and stage
if (NOT Boost_FOUND)
message(FATAL_ERROR "Did not find Boost. Boost is required for the core libraries, stage, sheets and krita.")
endif ()
if (APPLE)
find_package(Carbon REQUIRED)
endif ()
###########################
############################
## Optional dependencies ##
############################
###########################
##
## Check for OpenEXR
##
macro_optional_find_package(OpenEXR)
macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR)
##
## Look for OpenGL
##
set(HAVE_OPENGL 0)
if (USEOPENGL)
macro_optional_find_package(OpenGL)
if(OPENGL_FOUND)
message(STATUS "Found OpenGL: ${OPENGL_LIBRARIES}")
if(QT_QTOPENGL_FOUND)
message(STATUS "Found Qt OpenGL support")
set(HAVE_OPENGL 1)
else()
message(STATUS "Did NOT find Qt OpenGL support. Check your Qt configuration")
endif()
else()
message(STATUS "Did NOT find OpenGL libraries")
endif()
macro_log_feature(HAVE_OPENGL "OpenGL" "OpenGL support" "" FALSE "" "Required by Gemini, parts of Krita and optionally by flake")
endif()
##
## Test for GNU Scientific Library
##
macro_optional_find_package(GSL)
macro_log_feature(GSL_FOUND "GSL" "GNU Scientific Library" "http://www.gnu.org/software/gsl"
FALSE "1.7" "Required by Krita's Transform tool and Sheets' solver plugin")
macro_bool_to_01(GSL_FOUND HAVE_GSL)
configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h )
##
## Test for Phonon4Qt5
##
find_package(Phonon4Qt5)
macro_log_feature(Phonon4Qt5_FOUND "Phonon4Qt5" "Abstraction lib for multimedia applications" "http://www.kde.org/" FALSE "" "Required by Stage event actions and Videoshape plugin")
##
## Test for KF5CalendarCore
##
find_package(KF5CalendarCore CONFIG)
macro_log_feature(KF5CalendarCore_FOUND "KF5CalendarCore" "KDE Calendar Library" "http://www.kde.org/" FALSE "" "Required by Plan Ical export and optionally used by semantic item Event")
##
## Test for KF5Contacts
##
find_package(KF5Contacts CONFIG)
macro_log_feature(KF5Contacts_FOUND "KF5Contacts" "KDE Address book Library" "http://www.kde.org/" FALSE "" "Optionally used by semantic item Contact")
##
## Test for KF5AkonadiContact
##
find_package(KF5AkonadiContact CONFIG)
macro_log_feature(KF5AkonadiContact_FOUND "KF5AkonadiContact" "Library for Accessing Contacts stored in Akonadi" "http://www.kde.org/" FALSE "" "Optionally used by Plan")
##
## Test for KF5AkonadiCore
##
find_package(KF5Akonadi CONFIG)
macro_log_feature(KF5Akonadi_FOUND "KF5Akonadi" "Library for general Access to Akonadi" "http://www.kde.org/" FALSE "" "Optionally used by semantic items Event and Contact")
##
## Test for KGantt
##
macro_optional_find_package(KGantt 2.6.0)
macro_log_feature(KGantt_FOUND "KGantt" "Library for creating Gantt diagrams (part of KDiagram)" "http://www.kde.org/" FALSE "" "Required by Plan")
##
## Test for KChart
##
macro_optional_find_package(KChart 2.6.0)
macro_log_feature(KChart_FOUND "KChart" "Library for creating business charts (part of KDiagram)" "http://www.kde.org/" FALSE "" "Required by Chart shape and Plan")
##
## Test for KDb
##
macro_optional_find_package(KDb 1.0.0)
macro_log_feature(KDb_FOUND "KDb" "A database connectivity and creation framework"
"http://community.kde.org/KDb" FALSE "" "Required by Kexi and its plugins")
##
## Test for KReport
##
macro_optional_find_package(KReport 2.96.1)
if (KReport_FOUND AND KREPORT_SCRIPTING)
set(KReport_WITH_SCRIPTING_FOUND TRUE)
endif()
macro_log_feature(KReport_WITH_SCRIPTING_FOUND "KReport" "A framework for the creation and generation of reports in multiple formats (scripting support required)"
"http://community.kde.org/KReport" FALSE "" "Required by Kexi and Plan")
##
## Test for KProperty
##
macro_optional_find_package(KProperty 2.96.0)
macro_log_feature(KProperty_FOUND "KProperty" "A property editing framework with editor widget"
"http://community.kde.org/KProperty" FALSE "" "Required by Kexi")
##
## Test for eigen3
##
macro_optional_find_package(Eigen3)
macro_log_feature(EIGEN3_FOUND "Eigen" "C++ template library for linear algebra" "http://eigen.tuxfamily.org" FALSE "3.0" "Required by Calligra Sheets and Krita")
##
## Test for QCA2
##
macro_optional_find_package(Qca-qt5 2.1.0)
macro_log_feature(Qca-qt5_FOUND "QCA" "Qt Cryptographic Architecture" "http:/download.kde.org/stable/qca-qt5" FALSE "2.0" "Required for encrypted OpenDocument files and encrypted xls files support (available as a module in kdesupport)")
##
## Test for exiv2
##
set(EXIV2_MIN_VERSION "0.16")
macro_optional_find_package(Exiv2)
macro_log_feature(EXIV2_FOUND "Exiv2" "Image metadata library and tools" "http://www.exiv2.org" FALSE "0.16" "Required by Krita")
##
## Test for soprano
##
# QT5TODO: push for released (and maintained) Qt5 version of Soprano, T462, T461
# macro_optional_find_package(Soprano)
set(Soprano_FOUND FALSE)
macro_log_feature(Soprano_FOUND "Soprano" "RDF handling library" "http://soprano.sourceforge.net/" FALSE "" "Required to handle RDF metadata in ODF")
if(NOT Soprano_FOUND)
set(SOPRANO_INCLUDE_DIR "")
endif()
##
## Test for marble
##
set(MARBLE_MIN_VERSION "0.19.2")
macro_optional_find_package(CalligraMarble)
if(NOT MARBLE_FOUND)
set(CAN_USE_MARBLE FALSE)
set(MARBLE_INCLUDE_DIR "")
else()
set(CAN_USE_MARBLE TRUE)
add_definitions( -DCAN_USE_MARBLE )
##
## Marble changed addMarbleWidget to setMarbleWidget in MarbleControlBox.h
## with commit ea177ca. This is for compatibility with older versions.
##
find_file(MARBLECONTROLBOX_H MarbleControlBox.h ${MARBLE_INCLUDE_DIR} PATH_SUFFIXES marble)
if( MARBLECONTROLBOX_H )
file(READ ${MARBLECONTROLBOX_H} MARBLECONTROLBOX_H_CONTENT)
string(REGEX MATCH "setMarbleWidget" SETMARBLEWIDGET ${MARBLECONTROLBOX_H_CONTENT})
if( SETMARBLEWIDGET )
add_definitions(-DHAVE_SETMARBLEWIDGET)
endif()
else()
message( WARNING "MarbleControlBox.h not found, could not properly set the SETMARBLEWIDGET define." )
endif()
endif()
macro_log_feature(MARBLE_FOUND "Marble" "KDE4 World Globe Widget library" "http://techbase.kde.org/Projects/Marble/" FALSE "${MARBLE_MIN_VERSION}" "Required by RDF, Kexi Forms and Reports to show locations on a map")
##
## Test for lcms
##
macro_optional_find_package(LCMS2)
macro_log_feature(LCMS2_FOUND "LittleCMS" "Color management engine" "http://www.littlecms.com" FALSE "2.4" "Will be used for color management and is necesary for Krita")
if(LCMS2_FOUND)
if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 )
set(HAVE_LCMS24 TRUE)
endif()
set(HAVE_REQUIRED_LCMS_VERSION TRUE)
set(HAVE_LCMS2 TRUE)
endif()
##
## Test for Vc
##
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
macro_optional_find_package(Vc 0.6.70)
macro_log_feature(Vc_FOUND "Vc" "Portable, zero-overhead SIMD library for C++" "http://code.compeng.uni-frankfurt.de/projects/vc" FALSE "" "Required by the Krita for vectorization")
macro_bool_to_01(Vc_FOUND HAVE_VC)
macro_bool_to_01(PACKAGERS_BUILD DO_PACKAGERS_BUILD)
configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h )
if(HAVE_VC)
message(STATUS "Vc found!")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/vc")
include (VcMacros)
set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast")
#Handle Vc master
if(Vc_VERSION_MAJOR GREATER 0 OR Vc_VERSION_MINOR GREATER 7)
message(STATUS "Vc version is greater than 0.7, enabling AVX2 support")
if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG)
AddCompilerFlag("-std=c++11" _ok)
if(NOT _ok)
AddCompilerFlag("-std=c++0x" _ok)
endif()
endif()
macro(ko_compile_for_all_implementations_no_scalar _objs _src)
if(PACKAGERS_BUILD)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} -fPIC ONLY SSE2 SSSE3 SSE4_1 AVX AVX2)
else()
set(${_objs} ${_src})
endif()
endmacro()
macro(ko_compile_for_all_implementations _objs _src)
if(PACKAGERS_BUILD)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} -fPIC ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2)
else()
set(${_objs} ${_src})
endif()
endmacro()
else()
macro(ko_compile_for_all_implementations_no_scalar _objs _src)
if(PACKAGERS_BUILD)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} -fPIC ONLY SSE2 SSSE3 SSE4_1 AVX)
else()
set(${_objs} ${_src})
endif()
endmacro()
macro(ko_compile_for_all_implementations _objs _src)
if(PACKAGERS_BUILD)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} -fPIC ONLY Scalar SSE2 SSSE3 SSE4_1 AVX)
else()
set(${_objs} ${_src})
endif()
endmacro()
endif()
if (NOT PACKAGERS_BUILD)
# Optimize the whole Calligra for current architecture
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_DEFINITIONS}")
endif ()
endif()
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
##
## Test for Xinput
##
if(NOT WIN32 AND NOT APPLE)
set(REQUIRED_Xinput_FOUND ${X11_Xinput_FOUND})
else()
set(REQUIRED_Xinput_FOUND TRUE)
endif()
#Set the build of TextShape changetraker
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS})
if(WIN32)
# detect oxygen icon dir at configure time based on KDEDIRS - there may be different package installation locations
execute_process(COMMAND "${KDE4_KDECONFIG_EXECUTABLE}" --path icon OUTPUT_VARIABLE _dir ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
file(TO_CMAKE_PATH "${_dir}" __dir)
find_path(KDE4_ICON_DIR oxygen PATHS
${__dir}
)
message(STATUS "using oxygen application icons from ${KDE4_ICON_DIR}")
set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS}
ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} )
else()
set (KDE4_ICON_DIR ${CMAKE_INSTALL_PREFIX}/share/icons)
endif()
##
## Test for Fontconfig
##
## Only test if on non-Windows system
if(NOT WIN32 AND NOT APPLE)
macro_optional_find_package(Fontconfig)
macro_log_feature(FONTCONFIG_FOUND "Fontconfig" "Library for configuring and customizing font access" "http://fontconfig.org" FALSE "" "Required to handle exact font size")
endif()
##
## Test for Freetype
##
## Only test if on non-Windows system
if(NOT WIN32 AND NOT APPLE)
macro_optional_find_package(Freetype)
macro_log_feature(FREETYPE_FOUND "Freetype" "A Free, High-Quality, and Portable Font Engine" "http://www.freetype.org/" FALSE "" "Required to handle exact font size")
endif()
if(NOT FONTCONFIG_FOUND OR NOT FREETYPE_FOUND)
set(FONTCONFIG_INCLUDE_DIR "")
set(FREETYPE_INCLUDE_DIRS "")
else()
add_definitions( -DSHOULD_BUILD_FONT_CONVERSION )
endif()
##
## Test for Qt WebKitWidgets
##
macro_log_feature(Qt5Sql_FOUND "Qt Sql"
"Qt SQL module."
"http://qt.io" FALSE ""
"Optional for Sheets database connection")
##
## Test for Qt Webkit
##
macro_log_feature(QT_QTWEBKIT_FOUND "Qt Webkit" "Qt binding for Webkit, the HTML engine." "http://qt.io" FALSE "" "Required for the web shape, web Kexi widget and web report element")
if(QT_QTWEBKIT_FOUND)
add_definitions( -DCAN_USE_QTWEBKIT )
endif()
##
## Test for Qt WebKitWidgets
##
macro_log_feature(Qt5WebKitWidgets_FOUND "Qt WebkitWidgets" "QWidgets module for Webkit, the HTML engine." "http://qt.io" FALSE "" "Required for Stage")
##
## Test endianess
##
include (TestBigEndian)
test_big_endian(CMAKE_WORDS_BIGENDIAN)
##
## Test SharedMimeInfo
##
macro_optional_find_package(SharedMimeInfo)
macro_log_feature(SHARED_MIME_INFO_FOUND "SharedMimeInfo" "Shared Mime Info" "http://freedesktop.org/wiki/Software/shared-mime-info" FALSE "" "Required to determine file types OpenRaster (Krita default format), SVM or all of MSOOXML.")
##
## Test for Okular
##
macro_optional_find_package(Okular5 1.0.0)
macro_log_feature(Okular5_FOUND "Okular" "A unified document viewer" "http://okular.kde.org/" FALSE "" "Required to build the plugins for Okular")
##
## Test for librevenge
##
macro_optional_find_package(LibRevenge)
macro_log_feature(LIBREVENGE_FOUND "LibRevenge"
"A base library for writing document import filters"
"http://sf.net/p/libwpd/librevenge/" FALSE ""
"Required by various import filters"
)
##
## Test for libodfgen
##
macro_optional_find_package(LibOdfGen)
macro_log_feature(LIBODFGEN_FOUND "LibOdfGen"
"Open Document Format Generation Library"
"http://sf.net/p/libwpd/libodfgen/" FALSE ""
"Required by various import filters"
)
##
## Test for WordPerfect Document Library
##
macro_optional_find_package(LibWpd)
macro_log_feature(LIBWPD_FOUND "LibWpd"
"WordPerfect Document Library"
"http://libwpd.sourceforge.net/" FALSE ""
"Required by the Words WPD import filter"
)
##
## Test for WordPerfect Graphics Library
##
macro_optional_find_package(LibWpg)
macro_log_feature(LIBWPG_FOUND "LibWpg"
"WordPerfect Graphics Library"
"http://libwpg.sourceforge.net/" FALSE ""
"Required by the Karbon WPG import filter"
)
##
## Test for Microsoft Works Document Library
##
macro_optional_find_package(LibWps)
macro_log_feature(LIBWPS_FOUND "LibWps"
"Microsoft Works Document Library"
"http://libwps.sourceforge.net/" FALSE ""
"Required by the Words WPS import filter"
)
##
## Test for Microsoft Visio Document Library
##
macro_optional_find_package(LibVisio)
macro_log_feature(LIBVISIO_FOUND "LibVisio"
"Visio Import Filter Library"
"https://wiki.documentfoundation.org/DLP/Libraries/libvisio" FALSE ""
"Required by the Flow visio import filter"
)
##
## Test for Apple Keynote Document Library
##
macro_optional_find_package(LibEtonyek)
macro_log_feature(LIBETONYEK_FOUND "LibEtonyek"
"Apple Keynote Document Library"
"https://wiki.documentfoundation.org/DLP/Libraries/libetonyek" FALSE ""
"Required by the Stage keynote import filter"
)
##
## Test for qt-poppler
##
macro_optional_find_package(Poppler)
macro_log_feature( POPPLER_FOUND "Poppler-Qt5" "A PDF rendering library" "http://poppler.freedesktop.org" FALSE "" "Required by the Krita PDF filter, Karbon PDF import filter and CSTester PDF feature")
## The Karbon pdf-importer needs the not-officially-supported XPDF Headers
## Installing these is off by default in poppler sources, so lets make
## sure they're really there before trying to build the pdf import
if(POPPLER_FOUND)
find_path(POPPLER_XPDF_HEADERS poppler-config.h
HINTS ${POPPLER_INCLUDE_DIR} )
if(POPPLER_XPDF_HEADERS)
set(POPPLER_XPDF_HEADERS_FOUND TRUE)
endif()
macro_log_feature( POPPLER_XPDF_HEADERS_FOUND "poppler-qt5-xpdf-headers" "XPDF headers in the Poppler Qt5 interface library" "http://poppler.freedesktop.org" FALSE "" "Required by the Karbon PDF import filter")
endif()
##
## Test for libgit2 and Libqgit2
##
macro_optional_find_package(Libgit2)
macro_optional_find_package(Libqgit2)
##
## Generate a file for prefix information
##
###############################
################################
## Add Calligra helper macros ##
################################
###############################
include(MacroCalligraAddBenchmark)
include(MacroCalligraAddTest)
####################
#####################
## Define includes ##
#####################
####################
# WARNING: make sure that QT_INCLUDES is the first directory to be added to include_directory before
# any other include directory
# for config.h and <toplevel/foo.h> includes (if any?)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces )
set(KOVERSION_INCLUDES ${CMAKE_SOURCE_DIR}/libs/version
${CMAKE_BINARY_DIR}/libs/version
)
include_directories(${KOVERSION_INCLUDES})
# koplugin is at the bottom of the stack
set(KOPLUGIN_INCLUDES ${CMAKE_SOURCE_DIR}/libs/koplugin)
set(KUNDO2_INCLUDES ${CMAKE_SOURCE_DIR}/libs/kundo2)
# koodf is at the bottom of the stack
set(KOODF_INCLUDES ${CMAKE_SOURCE_DIR}/libs/odf
${CMAKE_BINARY_DIR}/libs/odf
${KOVERSION_INCLUDES}
)
# pigment depends on koplugin and lcms
set(PIGMENT_INCLUDES ${KOPLUGIN_INCLUDES}
${KOVERSION_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/pigment
${CMAKE_BINARY_DIR}/libs/pigment
${CMAKE_SOURCE_DIR}/libs/pigment/compositeops
${CMAKE_SOURCE_DIR}/libs/pigment/resources
${Boost_INCLUDE_DIRS}
)
# flake depends on koodf and pigment
set(FLAKE_INCLUDES ${CMAKE_SOURCE_DIR}/libs/flake
${KOODF_INCLUDES}
${PIGMENT_INCLUDES}
${KUNDO2_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/widgetutils
${CMAKE_SOURCE_DIR}/libs/flake/commands
${CMAKE_SOURCE_DIR}/libs/flake/tools
${CMAKE_SOURCE_DIR}/libs/flake/svg
${CMAKE_BINARY_DIR}/libs/flake)
# vectorimage
set(VECTORIMAGE_INCLUDES
${CMAKE_SOURCE_DIR}/libs/vectorimage
${CMAKE_SOURCE_DIR}/libs/vectorimage/libemf
${CMAKE_SOURCE_DIR}/libs/vectorimage/libsvm
${CMAKE_SOURCE_DIR}/libs/vectorimage/libwmf)
# KoText depends on koplugin, odf
set(KOTEXT_INCLUDES ${CMAKE_SOURCE_DIR}/libs/kotext
${CMAKE_BINARY_DIR}/libs/kotext
${CMAKE_SOURCE_DIR}/libs/kotext/changetracker
${CMAKE_SOURCE_DIR}/libs/kotext/styles
${CMAKE_SOURCE_DIR}/libs/kotext/opendocument
${SOPRANO_INCLUDE_DIR}
${FLAKE_INCLUDES}
${KOODF_INCLUDES})
# TextLayout depends on kotext
set(TEXTLAYOUT_INCLUDES ${KOTEXT_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/textlayout)
# Widgets depends on kotext and flake
set(KOWIDGETS_INCLUDES ${KOTEXT_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/widgetutils
${CMAKE_BINARY_DIR}/libs/widgetutils
${CMAKE_SOURCE_DIR}/libs/widgets
${CMAKE_BINARY_DIR}/libs/widgets)
# BasicFlakes depends on flake, widgets
set(BASICFLAKES_INCLUDES ${KOWIDGETS_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/basicflakes
${CMAKE_SOURCE_DIR}/libs/basicflakes/tools)
# komain depends on kotext & flake
set(KOMAIN_INCLUDES
${KOWIDGETS_INCLUDES}
${TEXTLAYOUT_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/main
${CMAKE_BINARY_DIR}/libs/main
${CMAKE_SOURCE_DIR}/libs/main/config)
set(KORDF_INCLUDES ${KOMAIN_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/rdf
)
set(KORDF_LIBS kordf)
if(SHOULD_BUILD_FEATURE_SCRIPTING)
set(KOKROSS_INCLUDES ${CMAKE_SOURCE_DIR}/libs/kokross ${CMAKE_BINARY_DIR}/libs/kokross)
endif()
# kopageapp
set(KOPAGEAPP_INCLUDES ${TEXTLAYOUT_INCLUDES}
${PIGMENT_INCLUDES}
${KOMAIN_INCLUDES}
${CMAKE_SOURCE_DIR}/libs/widgets
${CMAKE_SOURCE_DIR}/libs/kopageapp
${CMAKE_SOURCE_DIR}/libs/kopageapp/commands
${CMAKE_BINARY_DIR}/libs/kopageapp )
#############################################
#### filter libraries ####
#############################################
# libodf2
set(KOODF2_INCLUDES
${CMAKE_SOURCE_DIR}/filters/libodf2
${CMAKE_SOURCE_DIR}/filters/libodf2/chart
)
# libodfreader
set(KOODFREADER_INCLUDES
${CMAKE_SOURCE_DIR}/filters/libodfreader
)
###################################################
####################################################
## Detect which products/features can be compiled ##
####################################################
###################################################
if (NOT WIN32)
set(NOT_WIN TRUE)
endif()
if (NOT QT_MAC_USE_COCOA)
set(NOT_COCOA TRUE)
endif()
calligra_drop_product_on_bad_condition( LIB_KOMSOOXML
SHARED_MIME_INFO_FOUND "SharedMimeInfo not found (needed to install mimetypes)"
)
calligra_drop_product_on_bad_condition( FEATURE_RDF
Soprano_FOUND "Soprano not found"
)
calligra_drop_product_on_bad_condition( PART_STAGE
Qt5WebKitWidgets_FOUND "Qt5WebKitWidgets devel not found"
)
calligra_drop_product_on_bad_condition( PART_SHEETS
EIGEN3_FOUND "Eigen devel not found"
)
calligra_drop_product_on_bad_condition( APP_KRITA
EIGEN3_FOUND "Eigen devel not found"
EXIV2_FOUND "libexiv2 devel not found"
HAVE_REQUIRED_LCMS_VERSION "lcms devel not found"
SHARED_MIME_INFO_FOUND "SharedMimeInfo not found"
Boost_SYSTEM_FOUND "boost-system devel not found"
REQUIRED_Xinput_FOUND "Xinput devel not found "
)
calligra_drop_product_on_bad_condition( APP_KEXI
KDb_FOUND "KDb not found"
KReport_WITH_SCRIPTING_FOUND "KReport with scripting support not found"
KProperty_FOUND "KProperty not found"
Qt5UiTools_FOUND "Qt5UiTools not found"
)
calligra_drop_product_on_bad_condition( OKULAR_GENERATOR_ODP
Okular5_FOUND "Okular devel not found"
)
calligra_drop_product_on_bad_condition( OKULAR_GENERATOR_ODT
Okular5_FOUND "Okular devel not found"
)
calligra_drop_product_on_bad_condition( PLUGIN_CHARTSHAPE
KChart_FOUND "KChart devel not found"
)
calligra_drop_product_on_bad_condition( PLUGIN_VIDEOSHAPE
Phonon4Qt5_FOUND "Phonon4Qt5 devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_KEY_TO_ODP
LIBODFGEN_FOUND "libodfgen devel not found"
LIBETONYEK_FOUND "libetonyek devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_VISIO_TO_ODG
LIBODFGEN_FOUND "libodfgen devel not found"
LIBVISIO_FOUND "libvisio devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_WORDPERFECT_TO_ODT
LIBODFGEN_FOUND "libodfgen devel not found"
LIBWPD_FOUND "libwpd devel not found"
LIBWPG_FOUND "libwpg devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_WORKS_TO_ODT
LIBODFGEN_FOUND "libodfgen devel not found"
LIBWPS_FOUND "libwps devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_WPG_TO_SVG
LIBWPG_FOUND "libwpg devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_WPG_TO_ODG
LIBODFGEN_FOUND "libodfgen devel not found"
LIBWPG_FOUND "libwpg devel not found"
LIBREVENGE_FOUND "librevenge devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_PDF_TO_SVG
NOT_WIN "not supported on Windows"
POPPLER_XPDF_HEADERS_FOUND "poppler xpdf headers not found"
)
calligra_drop_product_on_bad_condition( FILTER_HTML_TO_ODS
NOT_WIN "not supported on Windows"
NOT_COCOA "not supported with Qt Cocoa"
KF5_KHtml_FOUND "KF5KHtml devel not found"
)
calligra_drop_product_on_bad_condition( FILTER_SHEETS_TO_HTML
NOT_WIN "not supported on Windows"
NOT_COCOA "not supported with Qt Cocoa"
)
calligra_drop_product_on_bad_condition( FILTER_KSPREAD_TO_LATEX
NOT_WIN "not supported on Windows"
NOT_COCOA "not supported with Qt Cocoa"
)
calligra_drop_product_on_bad_condition( APP_BRAINDUMP
NOT_WIN "unmaintained on Windows"
)
calligra_drop_product_on_bad_condition( APP_PLAN
KGantt_FOUND "KGantt devel not found"
KChart_FOUND "KChart devel not found"
KReport_FOUND "KReport not found"
KProperty_FOUND "KProperty not found"
)
calligra_drop_product_on_bad_condition( PLUGIN_CALLIGRAGEMINI_GIT
LIBGIT2_FOUND "libgit2 devel not found"
LIBQGIT2_FOUND "libqgit2 devel not found"
)
calligra_drop_product_on_bad_condition( PART_QTQUICK
USEOPENGL "USEOPENGL set to FALSE"
QT_QTOPENGL_FOUND "Qt OpenGL not found"
QT_QTDECLARATIVE_FOUND "QtDeclarative not found"
)
calligra_drop_product_on_bad_condition( APP_GEMINI
USEOPENGL "USEOPENGL set to FALSE"
QT_QTOPENGL_FOUND "Qt OpenGL not found"
)
#############################################
#### Backward compatibility BUILD_x=off ####
#############################################
# workaround: disable directly all products which might be activated by internal
# dependencies, but belong to scope of old flag
calligra_drop_products_on_old_flag(braindump APP_BRAINDUMP)
calligra_drop_products_on_old_flag(flow APP_FLOW)
calligra_drop_products_on_old_flag(karbon APP_KARBON)
calligra_drop_products_on_old_flag(kexi APP_KEXI)
calligra_drop_products_on_old_flag(krita APP_KRITA)
calligra_drop_products_on_old_flag(plan APP_PLAN)
calligra_drop_products_on_old_flag(sheets PART_SHEETS APP_SHEETS)
calligra_drop_products_on_old_flag(stage PART_STAGE APP_STAGE)
calligra_drop_products_on_old_flag(words PART_WORDS APP_WORDS)
#############################################
#### Temporarily broken products ####
#############################################
# If a product does not build due to some temporary brokeness disable it here,
# by calling calligra_disable_product with the product id and the reason,
# e.g.:
# calligra_disable_product(APP_KEXI "isn't buildable at the moment")
#############################################
#### Calculate buildable products ####
#############################################
calligra_drop_unbuildable_products()
#############################################
#### Setup product-depending vars ####
#############################################
if(SHOULD_BUILD_FEATURE_RDF)
add_definitions( -DSHOULD_BUILD_RDF )
endif()
###################
####################
## Subdirectories ##
####################
###################
add_subdirectory(words)
if (SHOULD_BUILD_APP_FLOW)
add_subdirectory(flow)
endif ()
add_subdirectory(stage)
if(SHOULD_BUILD_APP_KEXI)
add_subdirectory(kexi)
endif()
if(SHOULD_BUILD_APP_PLAN)
add_subdirectory(plan)
endif()
add_subdirectory(sheets)
if(SHOULD_BUILD_APP_KRITA)
add_subdirectory(krita)
endif()
if(SHOULD_BUILD_APP_KARBON)
add_subdirectory(karbon)
endif()
if(SHOULD_BUILD_APP_BRAINDUMP)
add_subdirectory(braindump)
endif()
if(SHOULD_BUILD_DOC)
add_subdirectory(doc)
endif()
if(SHOULD_BUILD_PART_QTQUICK)
add_subdirectory(qtquick)
endif()
if(SHOULD_BUILD_GEMINI)
add_subdirectory(gemini)
endif()
# non-app directories are moved here because they can depend on SHOULD_BUILD_{appname} variables set above
add_subdirectory(libs)
add_subdirectory(3rdparty)
add_subdirectory(interfaces)
add_subdirectory(cmake)
add_subdirectory(pics)
add_subdirectory(plugins)
add_subdirectory(servicetypes)
add_subdirectory(devtools)
add_subdirectory(extras)
add_subdirectory(filters)
add_subdirectory(data)
macro_display_feature_log()
calligra_product_deps_report("product_deps")
calligra_log_should_build()
add_custom_target(apidox doc/api/gendocs.pl WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h )
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel)
## temporary warning
if (WARN_ABOUT_CREATIVEONLY)
message(STATUS "WARNING:\n You are using a deprecated build flag,\n switch from \"-DCREATIVEONLY=ON\" to \"-DPRODUCTSET=CREATIVE\"\n and remove the line \"CREATIVEONLY:BOOL=ON\" from CMakeCache.txt")
message(STATUS "-------------------------------------------------------------------" )
endif ()
diff --git a/filters/libmso/writer.cpp b/filters/libmso/writer.cpp
index 120c4cf54be..4e806b73c59 100644
--- a/filters/libmso/writer.cpp
+++ b/filters/libmso/writer.cpp
@@ -1,90 +1,72 @@
/* This file is part of the KDE project
Copyright (C) 2010 by Nokia
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "writer.h"
#include "KoGenStyles.h"
-#include <QRegExp>
-
-namespace
-{
-QString format(double v)
-{
- static const QString f("%1");
- static const QString e("");
- static const QRegExp r("\\.?0+$");
- return f.arg(v, 0, 'f').replace(r, e);
-}
-
-QString mm(double v)
-{
- static const QString mm("mm");
- return format(v) + mm;
-}
-}//namespace
Writer::Writer(KoXmlWriter& xmlWriter, KoGenStyles& kostyles,
bool stylesxml_)
: xOffset(0),
yOffset(0),
scaleX(1),
scaleY(1),
g_rotation(0),
g_flipH(0),
g_flipV(0),
xml(xmlWriter),
styles(kostyles),
stylesxml(stylesxml_)
{
}
Writer Writer::transform(const QRectF& oldCoords, const QRectF &newCoords) const
{
Writer w(xml, styles, stylesxml);
w.xOffset = xOffset + oldCoords.x() * scaleX;
w.yOffset = yOffset + oldCoords.y() * scaleY;
w.scaleX = scaleX * oldCoords.width() / newCoords.width();
w.scaleY = scaleY * oldCoords.height() / newCoords.height();
w.xOffset -= w.scaleX * newCoords.x();
w.yOffset -= w.scaleY * newCoords.y();
w.g_rotation = g_rotation;
w.g_flipH = g_flipH;
w.g_flipV = g_flipV;
return w;
}
qreal Writer::vLength(qreal length) const
{
return length*scaleY;
}
qreal Writer::hLength(qreal length) const
{
return length*scaleX;
}
qreal Writer::vOffset(qreal offset) const
{
return yOffset + offset*scaleY;
}
qreal Writer::hOffset(qreal offset) const
{
return xOffset + offset*scaleX;
}
diff --git a/filters/sheets/xlsx/TestFormulaParser.cpp b/filters/sheets/xlsx/TestFormulaParser.cpp
index ac468aefe88..99ba9b31f5f 100644
--- a/filters/sheets/xlsx/TestFormulaParser.cpp
+++ b/filters/sheets/xlsx/TestFormulaParser.cpp
@@ -1,114 +1,114 @@
/*
* This file is part of Office 2007 Filters for Calligra
*
* Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
*
* Contact: Suresh Chande suresh.chande@nokia.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "TestFormulaParser.h"
-#include "qtest_kde.h"
+#include <QTest>
#include "FormulaParser.h"
#include "XlsxXmlWorksheetReader_p.h"
#include <sheets/Util.h>
void TestFormulaParser::testConvertFormula_data()
{
QTest::addColumn<QString>("xlsx");
QTest::addColumn<QString>("odf");
QTest::newRow("simple")
<< "A1"
<< "=A1";
QTest::newRow("argument delimiter")
<< "IF(A1=A2,1,2)"
<< "=IF(A1=A2;1;2)";
QTest::newRow("string")
<< "LEFT(\" Some ~text \",3)"
<< "=LEFT(\" Some ~text \";3)";
QTest::newRow("condition")
<< "IF(A15<$B$6*$B$4,A15+1,\"\")"
<< "=IF(A15<$B$6*$B$4;A15+1;\"\")";
QTest::newRow("union operator")
<< "AREAS((A1:A3,B3:C5))"
<< "=AREAS((A1:A3~B3:C5))";
QTest::newRow("nested function calls")
<< "IF(OR(C12=\"\",D12=\"\"),\"\",IF(C12=D12,\"Pass\",\"Fail\"))"
<< "=IF(OR(C12=\"\";D12=\"\");\"\";IF(C12=D12;\"Pass\";\"Fail\"))";
QTest::newRow("intersection operator")
<< "AREAS((A1:C5 B2:B3))"
<< "=AREAS((A1:C5!B2:B3))";
QTest::newRow("whitespace in normal arguments")
<< "IF(A1=A2, 2, \" IF(1,2) \")"
<< "=IF(A1=A2; 2; \" IF(1,2) \")";
QTest::newRow("multiple whitespace in normal arguments")
<< "IF(A1=A2 , 2 , \" IF(1,2) \")"
<< "=IF(A1=A2 ; 2 ; \" IF(1,2) \")";
QTest::newRow("mixing union and intersection")
<< "AREAS((A1:C5 B2:B3,C2:C3))"
<< "=AREAS((A1:C5!B2:B3~C2:C3))";
QTest::newRow("absolute positions")
<< "AREAS(($A$1:$A$3,$B3:C$5))"
<< "=AREAS(($A$1:$A$3~$B3:C$5))";
QTest::newRow("whole column")
<< "IF(A=B,A:B,2)"
<< "=IF(A=B;A$1:B$65536;2)";
QTest::newRow("Sheetname")
<< "=IF('Sheet 1'!A1,''Sheet '1''!A2,'''Sheet 1'''!A3"
<< "=IF('Sheet 1'!A1;'Sheet ''1'!A2;'Sheet 1'!A3";
QTest::newRow("intersection operator without extra parenthesis")
<< "AREAS(B2:D4 B2)"
<< "=AREAS(B2:D4!B2)";
QTest::newRow("intersection operator without extra parenthesis, extra whitespace")
<< "AREAS(B2:D4 B2)"
<< "=AREAS(B2:D4! B2)";
}
void TestFormulaParser::testConvertFormula()
{
QFETCH(QString, xlsx);
QFETCH(QString, odf);
QCOMPARE(Calligra::Sheets::MSOOXML::convertFormula(xlsx), odf);
}
void TestFormulaParser::testSharedFormulaReferences()
{
Sheet s1("Sheet1");
Cell* c1 = s1.cell(2, 5, true);
c1->formula = new FormulaImpl("=D6-E7");
Cell* c2 = s1.cell(12, 43, true);
QCOMPARE(MSOOXML::convertFormulaReference(c1, c2), QString("=N44-O45"));
static_cast<FormulaImpl*>(c1->formula)->m_formula = "=SUM(D6-E7)";
QCOMPARE(MSOOXML::convertFormulaReference(c1, c2), QString("=SUM(N44-O45)"));
static_cast<FormulaImpl*>(c1->formula)->m_formula = "=D6";
QCOMPARE(MSOOXML::convertFormulaReference(c1, c2), QString("=N44"));
static_cast<FormulaImpl*>(c1->formula)->m_formula = "=SUM(D6)";
QCOMPARE(MSOOXML::convertFormulaReference(c1, c2), QString("=SUM(N44)"));
static_cast<FormulaImpl*>(c1->formula)->m_formula = "=F8(H12)";
QCOMPARE(MSOOXML::convertFormulaReference(c1, c2), QString("=F8(R50)"));
}
-QTEST_KDEMAIN(TestFormulaParser, NoGUI)
+QTEST_GUILESS_MAIN(TestFormulaParser)
diff --git a/filters/sheets/xlsx/TestFormulaParser.h b/filters/sheets/xlsx/TestFormulaParser.h
index a6a32121c56..c51cd663f6a 100644
--- a/filters/sheets/xlsx/TestFormulaParser.h
+++ b/filters/sheets/xlsx/TestFormulaParser.h
@@ -1,39 +1,39 @@
/*
* This file is part of Office 2007 Filters for Calligra
*
* Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
*
* Contact: Suresh Chande suresh.chande@nokia.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef TEST_FORMULAPARSER_H
#define TEST_FORMULAPARSER_H
-#include <QtTest>
+#include <QObject>
class TestFormulaParser : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testConvertFormula_data();
void testConvertFormula();
void testSharedFormulaReferences();
};
#endif // TEST_FORMULAPARSER_H
diff --git a/filters/stage/powerpoint/tests/TestPPT.cpp b/filters/stage/powerpoint/tests/TestPPT.cpp
index f984062ff23..1020660bd2d 100644
--- a/filters/stage/powerpoint/tests/TestPPT.cpp
+++ b/filters/stage/powerpoint/tests/TestPPT.cpp
@@ -1,120 +1,122 @@
/* This file is part of the KDE project
* Copyright (C) 2013 Jos van den Oever <jos@vandenoever.info>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestPPT.h"
#include "PptToOdp.h"
#include "pole.h"
#include <KoOdf.h>
#include <QDebug>
#include <QDir>
+#include <QBuffer>
+#include <QTest>
// KDESRCDIR is defined by KDE build system
#ifndef KDESRCDIR
#define KDESRCDIR
#endif
namespace {
class TestRun {
public:
QString inputFilePath;
QString referenceDirPath;
void convert(QBuffer& buffer, KoStore::Backend backend);
void compareFiles(KoStore* created, const QString& path);
QByteArray readFile(const QString& path);
void test();
};
}
void
TestRun::convert(QBuffer& buffer, KoStore::Backend backend) {
KoStore* output = KoStore::createStore(&buffer, KoStore::Write,
KoOdf::mimeType(KoOdf::Presentation),
backend);
POLE::Storage storage(inputFilePath.toLatin1());
QVERIFY(storage.open());
PptToOdp ppttoodp(0, 0);
KoFilter::ConversionStatus status = ppttoodp.convert(storage, output);
QVERIFY(status == KoFilter::OK);
}
QByteArray
TestRun::readFile(const QString& path) {
QFile f(referenceDirPath + path);
f.open(QIODevice::ReadOnly);
QByteArray data = f.readAll();
f.close();
return data;
}
void
TestRun::compareFiles(KoStore* input, const QString& path) {
QVERIFY(input->hasFile(path));
QVERIFY(input->open(path));
const QByteArray created = input->read(input->size());
QVERIFY(created.size() == input->size());
QVERIFY(input->close());
const QByteArray reference = readFile(path);
QTextStream a(reference, QIODevice::ReadOnly);
a.setCodec("UTF-8");
QTextStream b(created, QIODevice::ReadOnly);
b.setCodec("UTF-8");
while (!a.atEnd()) {
const QString oldLine = a.readLine();
const QString newLine = b.readLine();
if (oldLine != newLine) {
qDebug() << "old: " << oldLine;
qDebug() << "new: " << newLine;
}
QVERIFY(oldLine == newLine);
}
QVERIFY(b.atEnd());
QVERIFY(reference.size() == created.size());
}
void
TestRun::test() {
inputFilePath = KDESRCDIR "data/diagram.ppt";
referenceDirPath = KDESRCDIR "data/diagram_odp/";
const KoStore::Backend backend = KoStore::Tar;
QBuffer buffer;
convert(buffer, backend);
qDebug() << buffer.isOpen();
buffer.close();
qDebug() << buffer.size();
KoStore* input = KoStore::createStore(&buffer, KoStore::Read,
KoOdf::mimeType(KoOdf::Presentation),
backend);
compareFiles(input, "content.xml");
compareFiles(input, "styles.xml");
compareFiles(input, "meta.xml");
compareFiles(input, "settings.xml");
compareFiles(input, "META-INF/manifest.xml");
}
void
TestPPT::testPPT() {
TestRun test;
test.test();
QVERIFY(true);
}
QTEST_MAIN(TestPPT)
diff --git a/filters/stage/powerpoint/tests/TestPPT.h b/filters/stage/powerpoint/tests/TestPPT.h
index fc6a6cb4034..4940be50fc2 100644
--- a/filters/stage/powerpoint/tests/TestPPT.h
+++ b/filters/stage/powerpoint/tests/TestPPT.h
@@ -1,33 +1,33 @@
/* This file is part of the KDE project
* Copyright (C) 2013 Jos van den Oever <jos@vandenoever.info>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTPPT_H
#define TESTPPT_H
-#include <QtTest>
+#include <QObject>
class TestPPT : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testPPT();
};
#endif
diff --git a/filters/words/msword-odf/graphicshandler.cpp b/filters/words/msword-odf/graphicshandler.cpp
index fd1af7c083c..092ef591b58 100644
--- a/filters/words/msword-odf/graphicshandler.cpp
+++ b/filters/words/msword-odf/graphicshandler.cpp
@@ -1,1287 +1,1280 @@
/* This file is part of the Calligra project
Copyright (C) 2003 Werner Trobin <trobin@kde.org>
Copyright (C) 2003 David Faure <faure@kde.org>
Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
Copyright (C) 2010, 2011 Matus Uzak <matus.uzak@ixonos.com>
Copyright (C) 2010, 2011 Matus Hanzes <matus.hanzes@ixonos.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the Library GNU General Public
version 2 of the License, or (at your option) version 3 or,
at the discretion of KDE e.V (which shall act as a proxy as in
section 14 of the GPLv3), any later version..
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "graphicshandler.h"
#include "generated/leinputstream.h"
#include "ODrawToOdf.h"
#include "drawstyle.h"
#include "pictures.h"
#include "msodraw.h"
#include "conversion.h"
#include "document.h"
#include "msdoc.h"
#include <KoGenStyle.h>
#include <kdebug.h>
#include <QColor>
#include <QByteArray>
//#define USE_OFFICEARTDGG_CONTAINER
//#define DEBUG_GHANDLER
using namespace wvWare;
using namespace MSO;
using Conversion::twipsToPt;
// Specifies the format of the picture data for the PICF structure.
enum
{
MM_SHAPE = 0x0064,
MM_SHAPEFILE = 0x0066
};
namespace
{
QString format(double v) {
static const QString f("%1");
static const QString e("");
static const QRegExp r("\\.?0+$");
return f.arg(v, 0, 'f').replace(r, e);
}
-QString pt(double v) {
- static const QString pt("pt");
- return format(v) + pt;
-}
-QString percent(double v) {
- return format(v) + '%';
-}
QString mm(double v) {
static const QString mm("mm");
return format(v) + mm;
}
}
/*
* ************************************************
* Drawing Writer
* ************************************************
*/
DrawingWriter::DrawingWriter(KoXmlWriter& xmlWriter, KoGenStyles& styles, bool stylesxml_)
: Writer(xmlWriter, styles, stylesxml_),
xLeft(0),
xRight(0),
yTop(0),
yBottom(0)
{
scaleX = 25.4 / 1440;
scaleY = 25.4 / 1440;
}
qreal DrawingWriter::vLength()
{
return Writer::vLength(yBottom - yTop);
}
qreal DrawingWriter::hLength()
{
return Writer::hLength(xRight - xLeft);
}
qreal DrawingWriter::vOffset()
{
return Writer::vOffset(yTop);
}
qreal DrawingWriter::hOffset()
{
return Writer::hOffset(xLeft);
}
void DrawingWriter::setRectangle(wvWare::Word97::FSPA& spa)
{
xLeft = spa.xaLeft;
xRight = spa.xaRight;
yTop = spa.yaTop;
yBottom = spa.yaBottom;
}
//FIXME: It doesn't make sense with current initialization, because when first
//time called, scaleX and scaleY are both set to zero! Both xOffset and
//yOffset doesn't change!
void DrawingWriter::setGroupRectangle(MSO::OfficeArtFSPGR& fspgr)
{
if (fspgr.xRight == fspgr.xLeft) {
return;
}
if (fspgr.yBottom == fspgr.yTop) {
return;
}
xOffset = xOffset + xLeft*scaleX;
yOffset = yOffset + yTop*scaleY;
scaleX = scaleX * (xRight - xLeft)/(qreal)(fspgr.xRight - fspgr.xLeft);
scaleY = scaleY * (yBottom - yTop)/(qreal)(fspgr.yBottom - fspgr.yTop);
xOffset = xOffset - fspgr.xLeft * scaleX;
yOffset = yOffset - fspgr.yTop * scaleY;
}
void DrawingWriter::setChildRectangle(MSO::OfficeArtChildAnchor& anchor)
{
xLeft = anchor.xLeft;
xRight = anchor.xRight;
yTop = anchor.yTop;
yBottom = anchor.yBottom;
}
void DrawingWriter::setRect(const QRect& rect)
{
xLeft = rect.left();
xRight = rect.right();
yTop = rect.top();
yBottom = rect.bottom();
}
/*
* ************************************************
* Graphics Handler
* ************************************************
*/
WordsGraphicsHandler::WordsGraphicsHandler(Document* doc,
KoXmlWriter* bodyWriter,
KoXmlWriter* manifestWriter,
KoStore* store, KoGenStyles* mainStyles,
const wvWare::Drawings* p_drawings,
const wvWare::Word97::FIB& fib)
: QObject()
, m_document(doc)
, m_store(store)
, m_currentWriter(bodyWriter)
, m_manifestWriter(manifestWriter)
, m_mainStyles(mainStyles)
, m_drawings(p_drawings)
, m_fib(fib)
, m_pOfficeArtHeaderDgContainer(0)
, m_pOfficeArtBodyDgContainer(0)
, m_processingGroup(false)
, m_objectType(Inline)
, m_rgbUid(0)
, m_zIndex(0)
, m_picf(0)
, m_pSpa(0)
{
kDebug(30513) ;
init();
}
WordsGraphicsHandler::~WordsGraphicsHandler()
{
delete m_pOfficeArtHeaderDgContainer;
delete m_pOfficeArtBodyDgContainer;
}
/*
* NOTE: All containers parsed by this function are optional.
*/
void WordsGraphicsHandler::init()
{
kDebug(30513);
parseOfficeArtContainers();
//create default GraphicStyle using information from OfficeArtDggContainer
defineDefaultGraphicStyle(m_mainStyles);
const OfficeArtBStoreContainer* blipStore = 0;
blipStore = m_officeArtDggContainer.blipStore.data();
if (!blipStore) {
#ifdef DEBUG_GHANDLER
kDebug(30513) << "Container of BLIPs not present.";
#endif
return;
}
//parse and store floating pictures
if (!parseFloatingPictures(blipStore)) {
m_store->enterDirectory("Pictures");
m_picNames = createPictures(m_store, m_manifestWriter, &blipStore->rgfb);
m_store->leaveDirectory();
}
}
DrawStyle WordsGraphicsHandler::getBgDrawStyle()
{
const OfficeArtSpContainer* shape = 0;
if (m_pOfficeArtBodyDgContainer) {
shape = (m_pOfficeArtBodyDgContainer->shape).data();
}
return DrawStyle(&m_officeArtDggContainer, 0, shape);
}
void WordsGraphicsHandler::emitTextBoxFound(unsigned int index, bool stylesxml)
{
emit textBoxFound(index, stylesxml);
}
QString WordsGraphicsHandler::handleInlineObject(const wvWare::PictureData& data, const bool isBulletPicture)
{
//TODO: The globalCP might be required to obtain the SPA structure for
//inline MS-ODRAW shapes with missing OfficeArtClientAnchor.
//TODO: It seems that both inline and floating objects have placement and
//dimensions stored in SPA structures. Check the OfficeArtClientAnchor for
//the index into plcfSpa. However the border information for inline
//msosptPictureFrame shapes is stored in the PICF struture.
kDebug(30513) ;
QString ret;
quint32 size = (data.picf->lcb - data.picf->cbHeader);
#ifdef DEBUG_GHANDLER
kDebug(30513) << "\nPICF DEBUG:"
<< "\nPICF size: 0x" << hex << data.picf->cbHeader
<< "\nOfficeArtInlineSpContainer size:" << dec << size
<< "\nStorage Format: 0x" << hex << data.picf->mfp.mm;
#endif
//the picture is store in some external file
if (data.picf->mfp.mm == MM_SHAPEFILE) {
if (!isBulletPicture) {
DrawingWriter out(*m_currentWriter, *m_mainStyles, m_document->writingHeader());
m_objectType = Inline;
m_picf = data.picf;
insertEmptyInlineFrame(out);
}
return ret;
}
// going to parse and process the Data stream content
LEInputStream* in = m_document->dataStream();
if (!in) {
kDebug(30513) << "Data stream not provided, no access to inline shapes!";
return ret;
}
if (data.fcPic > in->getSize()) {
kDebug(30513) << "OfficeArtInlineSpContainer offset out of range, skipping!";
return ret;
}
#ifdef DEBUG_GHANDLER
kDebug(30513) << "\nCurrent stream position:" << in->getPosition()
<< "\nOfficeArtInlineSpContainer offset:" << dec << data.fcPic;
#endif
// parse the OfficeArtInlineSpContainer and rewind the stream
LEInputStream::Mark _zero;
_zero = in->setMark();
in->skip(data.fcPic);
OfficeArtInlineSpContainer co;
try {
parseOfficeArtInlineSpContainer(*in, co);
} catch (const IOException& e) {
kDebug(30513) << e.msg;
in->rewind(_zero);
return ret;
} catch (...) {
kWarning(30513) << "Warning: Caught an unknown exception!";
in->rewind(_zero);
return ret;
}
in->rewind(_zero);
int n = (data.fcPic + size) - in->getPosition();
if (n) {
kDebug(30513) << n << "bytes left while parsing OfficeArtInlineSpContainer";
}
PictureReference ref;
// store picture data if present and update m_picNames
m_store->enterDirectory("Pictures");
foreach (const OfficeArtBStoreContainerFileBlock& block, co.rgfb) {
const OfficeArtFBSE* fbse = block.anon.get<MSO::OfficeArtFBSE>();
if (!fbse) {
kDebug(30513) << "Warning: FBSE container not found, skipping ";
} else {
//check if this BLIP is already in hash table
if (m_picNames.contains(fbse->rgbUid)) {
ref.uid = fbse->rgbUid;
ref.name = m_picNames[fbse->rgbUid];
continue;
} else {
ref = savePicture(block, m_store);
if (ref.name.length() == 0) {
kDebug(30513) << "empty name in picture reference";
break;
}
m_manifestWriter->addManifestEntry("Pictures/" + ref.name, ref.mimetype);
m_picNames[ref.uid] = ref.name;
}
}
}
m_store->leaveDirectory();
if (isBulletPicture) {
return ref.name;
}
bool inStylesXml = m_document->writingHeader();
DrawingWriter out(*m_currentWriter, *m_mainStyles, inStylesXml);
//global attributes
m_objectType = Inline;
m_rgbUid = ref.uid;
m_picf = data.picf;
const OfficeArtSpContainer* o = &(co.shape);
processDrawingObject(*o, out);
return ret;
}
void WordsGraphicsHandler::handleFloatingObject(unsigned int globalCP)
{
#ifdef DEBUG_GHANDLER
kDebug(30513) << "globalCP" << globalCP ;
#endif
if (!m_drawings) {
return;
}
const PLCF<Word97::FSPA>* plcfSpa = 0;
MSO::OfficeArtDgContainer* dg = 0;
uint threshold = 0;
if (m_document->writingHeader()) {
plcfSpa = m_drawings->getSpaHdr();
dg = m_pOfficeArtHeaderDgContainer;
threshold = m_fib.ccpText + m_fib.ccpFtn;
} else {
plcfSpa = m_drawings->getSpaMom();
dg = m_pOfficeArtBodyDgContainer;
}
if (!plcfSpa) {
kDebug(30513) << "MISSING plcfSpa!";
return;
}
if (!dg) {
kDebug(30513) << "MISSING OfficeArtDgContainer!";
return;
}
PLCFIterator<Word97::FSPA> it(plcfSpa->at(0));
for (size_t i = 0; i < plcfSpa->count(); i++, ++it) {
#ifdef DEBUG_GHANDLER
kDebug(30513) << "FSPA start:" << it.currentStart();
kDebug(30513) << "FSPA spid:" << it.current()->spid;
#endif
if ((it.currentStart() + threshold) == globalCP) {
bool inStylesXml = m_document->writingHeader();
DrawingWriter out(*m_currentWriter, *m_mainStyles, inStylesXml);
//global attributes
m_objectType = Floating;
m_pSpa = it.current();
m_zIndex = 1;
locateDrawing((dg->groupShape).data(), it.current(), (uint)it.current()->spid, out);
//reset global attributes
m_pSpa = 0;
return;
}
}
}
void WordsGraphicsHandler::locateDrawing(const MSO::OfficeArtSpgrContainer* spgr,
wvWare::Word97::FSPA* spa,
uint spid,
DrawingWriter& out)
{
if (!spgr) {
return;
}
//FIXME: combine childAnchor, shapeGroup coordinates with information from
//clientAnchor pointing to the SPA structure!
//NOTE: The OfficeArtSpgrContainer record specifies a container for groups
//(4) of shapes. The group (4) container contains a variable number of
//shape containers and other group (4) containers. Each group (4) is a
//shape. The first container MUST be an OfficeArtSpContainer record, which
//MUST contain shape information for the group. MS-ODRAW, 2.2.16
const OfficeArtSpContainer* sp = spgr->rgfb[0].anon.get<OfficeArtSpContainer>();
if (sp && (sp->shapeProp.spid == spid)) {
kDebug(30513) << "An unprocessed shape referred from text, ignoring!";
return;
}
for(int i = 1; i < spgr->rgfb.size(); i++) {
const OfficeArtSpgrContainerFileBlock& co = spgr->rgfb[i];
if (co.anon.is<OfficeArtSpgrContainer>()) {
sp = (*co.anon.get<OfficeArtSpgrContainer>()).rgfb[0].anon.get<OfficeArtSpContainer>();
if (sp && sp->shapeProp.spid == spid) {
processGroupShape(*co.anon.get<OfficeArtSpgrContainer>(), out);
m_processingGroup = false;
break;
} else {
m_zIndex = m_zIndex + (*co.anon.get<OfficeArtSpgrContainer>()).rgfb.size();
}
} else {
sp = co.anon.get<OfficeArtSpContainer>();
if (sp && sp->shapeProp.spid == spid) {
out.setRectangle(*spa);
processDrawingObject(*sp, out);
break;
}
m_zIndex++;
}
}
}
QRect WordsGraphicsHandler::getRect(const MSO::OfficeArtSpContainer &o)
{
if (o.clientAnchor) {
const DocOfficeArtClientAnchor* a = o.clientAnchor->anon.get<DocOfficeArtClientAnchor>();
if (!a) {
return QRect();
}
const PLCF<wvWare::Word97::FSPA>* plcfSpa = 0;
if (m_document->writingHeader()) {
plcfSpa = m_drawings->getSpaHdr();
} else {
plcfSpa = m_drawings->getSpaMom();
}
PLCFIterator<wvWare::Word97::FSPA> it(plcfSpa->at(a->clientAnchor));
const wvWare::Word97::FSPA* spa = it.current();
Q_ASSERT(m_pSpa == spa);
return QRect(spa->xaLeft, spa->yaTop, spa->xaRight - spa->xaLeft, spa->yaBottom - spa->yaTop);
}
else if (o.childAnchor) {
const MSO::OfficeArtChildAnchor& r = *o.childAnchor;
return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
} else {
return QRect();
}
}
void WordsGraphicsHandler::processGroupShape(const MSO::OfficeArtSpgrContainer& o, DrawingWriter& out)
{
if (o.rgfb.size() < 2) {
return;
}
const OfficeArtSpContainer *sp = o.rgfb[0].anon.get<OfficeArtSpContainer>();
if (sp && sp->shapeGroup) {
QRect oldCoords = getRect(*sp);
if (oldCoords.isValid()) {
out.setRect(oldCoords);
//process shape information for the group
out.setGroupRectangle(*sp->shapeGroup);
}
}
//create graphic style for the group shape
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
DrawStyle ds(&m_officeArtDggContainer, 0, sp);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
defineWrappingAttributes(style, ds);
styleName = out.styles.insert(style, "gr");
out.xml.startElement("draw:g");
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
m_processingGroup = true;
for (int i = 1; i < o.rgfb.size(); ++i) {
if (o.rgfb[i].anon.is<OfficeArtSpContainer>()) {
OfficeArtSpContainer sp = *o.rgfb[i].anon.get<OfficeArtSpContainer>();
if (sp.childAnchor) {
out.setChildRectangle(*sp.childAnchor);
}
processDrawingObject(sp, out);
} else {
processGroupShape(*o.rgfb[i].anon.get<OfficeArtSpgrContainer>(), out);
}
}
out.xml.endElement(); // draw:g
}
void WordsGraphicsHandler::processDrawingObject(const MSO::OfficeArtSpContainer& o, DrawingWriter out)
{
kDebug(30513);
DrawStyle ds(0, 0, &o);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
#ifdef DEBUG_GHANDLER
kDebug(30513) << "shapeType: 0x" << hex << o.shapeProp.rh.recInstance;
kDebug(30513) << "grupShape: " << o.shapeProp.fGroup;
kDebug(30513) << "Selected properties: ";
kDebug(30513) << "pib: " << ds.pib();
#endif
switch (o.shapeProp.rh.recInstance) {
case msosptTextBox:
#ifdef DEBUG_GHANDLER
kDebug(30513)<< "processing TextBox";
#endif
processTextBox(o, out);
break;
case msosptRectangle:
if (ds.fHorizRule()) {
#ifdef DEBUG_GHANDLER
kDebug(30513)<< "processing Line";
#endif
processLineShape(o, out);
} else {
odrawtoodf.processDrawingObject(o, out);
}
break;
case msosptPictureFrame:
#ifdef DEBUG_GHANDLER
kDebug(30513)<< "processing PictureFrame";
#endif
if (m_objectType == Inline) {
processInlinePictureFrame(o, out);
} else {
processFloatingPictureFrame(o, out);
}
break;
case msosptHostControl:
#ifdef DEBUG_GHANDLER
kDebug(30513)<< "processing Host Control";
#endif
processTextBox(o, out);
break;
default:
odrawtoodf.processDrawingObject(o, out);
break;
}
}
void WordsGraphicsHandler::parseOfficeArtContainers()
{
kDebug(30513);
if (!m_fib.lcbDggInfo) return;
POLE::Stream& stream = m_document->poleTableStream();
if (stream.fail()) {
kDebug(30513) << "Table stream not provided, no access to OfficeArt file records!";
return;
}
QByteArray array;
QBuffer buffer;
array.resize(m_fib.lcbDggInfo);
stream.seek(m_fib.fcDggInfo);
unsigned long n = stream.read((unsigned char*) array.data(), m_fib.lcbDggInfo);
if (n != m_fib.lcbDggInfo) {
kError(30513) << "Error while reading from " << stream.fullName().data() << "stream";
return;
}
buffer.setData(array);
buffer.open(QIODevice::ReadOnly);
LEInputStream in(&buffer);
//parse OfficeArfDggContainer from msdoc
try {
parseOfficeArtDggContainer(in, m_officeArtDggContainer);
}
catch (const IOException& e) {
kDebug(30513) << "Caught IOException while parsing OfficeArtDggContainer.";
kDebug(30513) << e.msg;
return;
}
catch (...) {
kDebug(30513) << "Caught UNKNOWN exception while parsing OfficeArtDggContainer.";
return;
}
#ifdef DEBUG_GHANDLER
kDebug(30513) << "OfficeArtDggContainer [ OK ]" ;
#endif
// parse drawingsVariable from msdoc
// 0 - next OfficeArtDgContainer belongs to Main document;
// 1 - next OfficeArtDgContainer belongs to Header Document
unsigned char drawingsVariable = 0;
try {
drawingsVariable = in.readuint8();
}
catch (const IOException& e) {
kDebug(30513) << "Caught IOException while parsing DrawingsVariable.";
kDebug(30513) << e.msg;
return;
}
catch (...) {
kDebug(30513) << "Caught UNKNOWN exception while parsing DrawingsVariable.";
return;
}
//parse OfficeArfDgContainer from msdoc
OfficeArtDgContainer *pDgContainer = 0;
try {
pDgContainer = new OfficeArtDgContainer();
if (drawingsVariable == 0) {
m_pOfficeArtBodyDgContainer = pDgContainer;
} else {
m_pOfficeArtHeaderDgContainer = pDgContainer;
}
parseOfficeArtDgContainer(in, *pDgContainer);
}
catch (const IOException& e) {
kDebug(30513) << "Caught IOException while parsing OfficeArtDgContainer.";
kDebug(30513) << e.msg;
return;
}
catch (...) {
kDebug(30513) << "Caught UNKNOWN exception while parsing OfficeArtDgContainer.";
return;
}
#ifdef DEBUG_GHANDLER
kDebug(30513) << "OfficeArtDgContainer (" << (drawingsVariable ? "Headers" : "Body") << ") [ OK ]";
#endif
// parse drawingsVariable from msdoc
// 0 - next OfficeArtDgContainer belongs to Main Document
// 1 - next OfficeArtDgContainer belongs to Header Document
try {
drawingsVariable = in.readuint8();
}
catch (const IOException& e) {
kDebug(30513) << "Caught IOException while parsing the 2nd DrawingsVariable.";
kDebug(30513) << e.msg;
return;
}
catch (...) {
kDebug(30513) << "Caught UNKNOWN exception while parsing the 2nd DrawingsVariable.";
return;
}
//parse OfficeArfDgContainer from msdoc
pDgContainer = 0;
try {
pDgContainer = new OfficeArtDgContainer();
if (drawingsVariable == 0) {
if (m_pOfficeArtBodyDgContainer != 0){
delete m_pOfficeArtBodyDgContainer;
}
m_pOfficeArtBodyDgContainer = pDgContainer;
} else {
if (m_pOfficeArtHeaderDgContainer != 0) {
delete m_pOfficeArtHeaderDgContainer;
}
m_pOfficeArtHeaderDgContainer = pDgContainer;
}
parseOfficeArtDgContainer(in, *pDgContainer);
}
catch (const IOException& e) {
kDebug(30513) << "Caught IOException while parsing the 2nd OfficeArtDgContainer.";
kDebug(30513) << e.msg;
return;
}
catch (...) {
kDebug(30513) << "Caught UNKNOWN exception while parsing the 2nd OfficeArtDgContainer.";
return;
}
#ifdef DEBUG_GHANDLER
kDebug(30513) << "OfficeArtDgContainer (" << (drawingsVariable ? "Headers" : "Body") << ") [ OK ]";
#endif
quint32 r = buffer.size() - in.getPosition();
if (r > 0) {
kError(30513) << "Error:" << r << "bytes left to parse from the OfficeArtContent!";
}
}
int WordsGraphicsHandler::parseFloatingPictures(const OfficeArtBStoreContainer* blipStore)
{
kDebug(30513);
if (!blipStore) return(1);
// WordDocument stream equals the Delay stream, [MS-DOC] — v20101219
LEInputStream& in = m_document->wdocumentStream();
for (int i = 0; i < blipStore->rgfb.size(); i++) {
OfficeArtBStoreContainerFileBlock block = blipStore->rgfb[i];
//Parse content of the Delay stream by using offsets from OfficeArtFBSE
//containers. Not parsing Blip store because MD4 digests in
//OfficeArtFBSE happen to be out-dated, which complicates the pib to
//picture path association.
if (block.anon.is<OfficeArtFBSE>()) {
OfficeArtFBSE* fbse = block.anon.get<OfficeArtFBSE>();
if (!fbse->embeddedBlip) {
//NOTE: An foDelay value of 0xffffffff specifies that the file
//is not in the delay stream and cRef must be zero. A cRef
//value of 0x00000000 specifies an empty slot in the
//OfficeArtBStoreContainer.
if (fbse->foDelay == 0xffffffff) {
#ifdef DEBUG_GHANDLER
kDebug(30513) << "File not in the delay stream, continuing.";
#endif
continue;
}
if (!fbse->cRef) {
#ifdef DEBUG_GHANDLER
kDebug(30513) << "Empty slot, continuing.";
#endif
continue;
}
LEInputStream::Mark _zero;
_zero = in.setMark();
in.skip(fbse->foDelay);
//let's check the record header if there's a BLIP stored
LEInputStream::Mark _m;
_m = in.setMark();
OfficeArtRecordHeader rh;
try {
parseOfficeArtRecordHeader(in, rh);
} catch (const IOException& e) {
kDebug(30513) << e.msg;
in.rewind(_zero);
continue;
} catch (...) {
kWarning(30513) << "Warning: Caught an unknown exception!";
in.rewind(_zero);
continue;
}
in.rewind(_m);
if ( !(rh.recType >= 0xF018 && rh.recType <= 0xF117) ) {
continue;
}
fbse->embeddedBlip = QSharedPointer<OfficeArtBlip>(new OfficeArtBlip(fbse));
try {
parseOfficeArtBlip(in, *(fbse->embeddedBlip.data()));
} catch (const IOException& e) {
kDebug(30513) << e.msg;
in.rewind(_zero);
continue;
} catch (...) {
kWarning(30513) << "Warning: Caught an unknown exception!";
in.rewind(_zero);
continue;
}
in.rewind(_zero);
}
}
}
return(0);
}
QString WordsGraphicsHandler::getPicturePath(quint32 pib) const
{
quint32 offset = 0;
QByteArray rgbUid = getRgbUid(m_officeArtDggContainer, pib, offset);
if (rgbUid.length()) {
if (m_picNames.contains(rgbUid)) {
return "Pictures/" + m_picNames[rgbUid];
} else {
qDebug() << "UNKNOWN picture reference!";
}
}
return QString();
}
void WordsGraphicsHandler::defineDefaultGraphicStyle(KoGenStyles* styles)
{
// write style <style:default-style style:family="graphic">
KoGenStyle style(KoGenStyle::GraphicStyle, "graphic");
style.setDefaultStyle(true);
DrawStyle ds(&m_officeArtDggContainer);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, *styles);
styles->insert(style);
MSO::OfficeArtCOLORREF fc = ds.fillColor();
QColor color = QColor(fc.red, fc.green, fc.blue);
m_document->updateBgColor(color.name());
}
void WordsGraphicsHandler::defineWrappingAttributes(KoGenStyle& style, const DrawStyle& ds)
{
if (m_processingGroup) return;
if (m_objectType == Inline) return;
const KoGenStyle::PropertyType gt = KoGenStyle::GraphicType;
wvWare::Word97::FSPA* spa = m_pSpa;
// style:number-wrapped-paragraphs
// style:run-through
// style:wrap
// style:wrap-contour
// style:wrap-contour-mode
// style:wrap-dynamic-threshold
if (spa != 0) {
bool check_wrk = false;
switch (spa->wr) {
case 0: //wrap around the object
case 2: //square wrapping
check_wrk = true;
break;
case 1: //top and bottom wrapping
style.addProperty("style:wrap", "none", gt);
break;
case 3: //in front or behind the text
style.addProperty("style:wrap", "run-through", gt);
//check if shape is behind the text
if ((spa->fBelowText == 1) || (ds.fBehindDocument())) {
style.addProperty("style:run-through", "background", gt);
} else {
style.addProperty("style:run-through", "foreground", gt);
}
break;
case 4: //tight wrapping
check_wrk = true;
style.addProperty("style:wrap-contour", "true", gt);
style.addProperty("style:wrap-contour-mode", "outside", gt);
break;
case 5: //through wrapping
check_wrk = true;
style.addProperty("style:wrap-contour", "true", gt);
style.addProperty("style:wrap-contour-mode", "full", gt);
break;
}
//check details of the text wrapping around this shape
if (check_wrk) {
switch (spa->wrk) {
case 0:
style.addProperty("style:wrap", "parallel", gt);
break;
case 1:
style.addProperty("style:wrap", "left", gt);
break;
case 2:
style.addProperty("style:wrap", "right", gt);
break;
case 3:
style.addProperty("style:wrap", "biggest", gt);
break;
}
}
// ODF-1.2: specifies the number of paragraphs that can wrap around a
// frame if wrap mode is in {left, right, parallel, dynamic} and anchor
// type is in {char, paragraph}
if ((spa->wr != 1) && (spa->wr != 3)) {
style.addProperty("style:number-wrapped-paragraphs", "no-limit");
}
} else {
style.addProperty("style:wrap", "run-through", gt);
if (ds.fBehindDocument()) {
style.addProperty("style:run-through", "background", gt);
} else {
style.addProperty("style:run-through", "foreground", gt);
}
}
}
void WordsGraphicsHandler::definePositionAttributes(KoGenStyle& style, const DrawStyle& ds)
{
if (m_processingGroup) return;
const KoGenStyle::PropertyType gt = KoGenStyle::GraphicType;
if (m_objectType == Inline) {
style.addProperty("style:vertical-rel", "baseline", gt);
style.addProperty("style:vertical-pos", "top", gt);
} else {
style.addProperty("style:horizontal-pos", getHorizontalPos(ds.posH()), gt);
style.addProperty("style:horizontal-rel", getHorizontalRel(ds.posRelH()), gt);
style.addProperty("style:vertical-pos", getVerticalPos(ds.posV()), gt);
style.addProperty("style:vertical-rel", getVerticalRel(ds.posRelV()), gt);
}
}
void WordsGraphicsHandler::setAnchorTypeAttribute(DrawingWriter& out)
{
if (m_processingGroup) return;
// text:anchor-type
if (m_objectType == Inline) {
out.xml.addAttribute("text:anchor-type", "as-char");
} else {
out.xml.addAttribute("text:anchor-type", "char");
}
}
void WordsGraphicsHandler::setZIndexAttribute(DrawingWriter& out)
{
if (m_processingGroup) return;
// draw:z-index
if (m_objectType == Floating) {
out.xml.addAttribute("draw:z-index", m_zIndex);
} else {
out.xml.addAttribute("draw:z-index", 0);
}
}
void WordsGraphicsHandler::processTextBox(const MSO::OfficeArtSpContainer& o, DrawingWriter out)
{
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
const MSO::OfficeArtDggContainer *dgg = 0;
#ifdef USE_OFFICEARTDGG_CONTAINER
dgg = &m_officeArtDggContainer;
#endif
DrawStyle ds(dgg, 0, &o);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
defineWrappingAttributes(style, ds);
styleName = out.styles.insert(style);
out.xml.startElement("draw:frame");
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
switch(ds.txflTextFlow()) {
case 1: //msotxflTtoBA up-down
case 3: //msotxflTtoBN up-down
case 5: //msotxflVertN up-down
out.xml.addAttribute("svg:width", mm(out.vLength()));
out.xml.addAttribute("svg:height", mm(out.hLength()));
out.xml.addAttribute("draw:transform","matrix(0 1 -1 0 " +
mm(((Writer *)&out)->hOffset(out.xRight)) + " " + mm(out.vOffset()) + ")");
break;
case 2: //msotxflBtoT down-up
out.xml.addAttribute("svg:width", mm(out.vLength()));
out.xml.addAttribute("svg:height", mm(out.hLength()));
out.xml.addAttribute("draw:transform","matrix(0 -1 1 0 " +
mm(out.hOffset()) + " " + mm(((Writer *)&out)->vOffset(out.yBottom)) + ")");
break;
default : //standard text flow
out.xml.addAttribute("svg:width", mm(out.hLength()));
out.xml.addAttribute("svg:height", mm(out.vLength()));
out.xml.addAttribute("svg:x", mm(out.hOffset()));
out.xml.addAttribute("svg:y", mm(out.vOffset()));
}
out.xml.startElement("draw:text-box");
// Especially Word8 files with (nFib == Word8nFib2) do not provide
// an OfficeArtClientTextBox.
bool textIdValid = false;
quint32 textId = 0;
if (o.clientTextbox) {
const DocOfficeArtClientTextBox* tb = o.clientTextbox->anon.get<DocOfficeArtClientTextBox>();
if (tb) {
textId = tb->clientTextBox;
textIdValid = true;
} else {
kDebug(30513) << "DocOfficeArtClientTextBox missing!";
}
} else {
if (ds.iTxid() < 0) {
kDebug(30513) << "lTxid property - negative text identifier!";
} else {
textId = (quint32)ds.iTxid();
textIdValid = true;
}
}
if (textIdValid) {
emit textBoxFound(((textId / 0x10000) - 1), out.stylesxml);
}
out.xml.endElement(); //draw:text-box
out.xml.endElement(); //draw:frame
}
void WordsGraphicsHandler::processInlinePictureFrame(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
{
kDebug(30513) ;
// Shape instance contained in OfficeArtInlineSpContainer. BLIP properties
// contained in o.shapePrimaryOptions or o.shapeTertiaryOptions1 are stored
// in the order they are encountered, and the property values
// OfficeArtFOPTE.opid.fBid, OfficeArtFOPTE.opid.fComplex, and
// OfficeArtFOPTE.op MUST be ignored. [MS-ODRAW] — v20101219
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
const MSO::OfficeArtDggContainer *dgg = 0;
#ifdef USE_OFFICEARTDGG_CONTAINER
dgg = &m_officeArtDggContainer;
#endif
DrawStyle ds(dgg, 0, &o);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
style.addProperty("fo:border-top", Conversion::setBorderAttributes(m_picf->brcTop));
style.addProperty("fo:border-left", Conversion::setBorderAttributes(m_picf->brcLeft));
style.addProperty("fo:border-bottom", Conversion::setBorderAttributes(m_picf->brcBottom));
style.addProperty("fo:border-right", Conversion::setBorderAttributes(m_picf->brcRight));
// NOTE: The default margin-left/margin-right values DO NOT make sense for
// inline pictures, also after conversion of test files to DOCX, both
// attributes were set to ZEROs. Default margin-top/margin-bottom is ZERO.
style.addPropertyPt("fo:margin", 0);
styleName = out.styles.insert(style);
// A diagram drawing canvas placed inline with surrounding text.
if (ds.fPseudoInline()) {
out.xml.startElement("draw:rect");
} else {
out.xml.startElement("draw:frame");
}
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
double hscale = m_picf->mx / 1000.0;
double vscale = m_picf->my / 1000.0;
out.xml.addAttributePt("svg:width", twipsToPt(m_picf->dxaGoal) * hscale);
out.xml.addAttributePt("svg:height", twipsToPt(m_picf->dyaGoal) * vscale);
QString name = m_picNames.value(m_rgbUid);
QString url;
if (!name.isEmpty()) {
url.append("Pictures/");
url.append(name);
} else {
// if the image cannot be found, just place an empty frame
out.xml.endElement(); //draw:frame (draw:rect)
return;
}
//TODO: process border information (complex properties)
out.xml.startElement("draw:image");
out.xml.addAttribute("xlink:href", url);
out.xml.addAttribute("xlink:type", "simple");
out.xml.addAttribute("xlink:show", "embed");
out.xml.addAttribute("xlink:actuate", "onLoad");
out.xml.endElement(); //draw:image
out.xml.endElement(); //draw:frame
return;
}
void WordsGraphicsHandler::processFloatingPictureFrame(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
{
kDebug(30513) ;
const MSO::OfficeArtDggContainer *dgg = 0;
#ifdef USE_OFFICEARTDGG_CONTAINER
dgg = &m_officeArtDggContainer;
#endif
DrawStyle ds(dgg, 0, &o);
// A value of 0x00000000 MUST be ignored. [MS-ODRAW] — v20101219
if (!ds.pib()) return;
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
defineWrappingAttributes(style, ds);
styleName = out.styles.insert(style);
out.xml.startElement("draw:frame");
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
out.xml.addAttribute("svg:width", mm(out.hLength()));
out.xml.addAttribute("svg:height", mm(out.vLength()));
out.xml.addAttribute("svg:x", mm(out.hOffset()));
out.xml.addAttribute("svg:y", mm(out.vOffset()));
QString url = getPicturePath(ds.pib());
//if the image cannot be found, just place an empty frame
if (url.isEmpty()) {
out.xml.endElement(); //draw:frame
return;
}
out.xml.startElement("draw:image");
out.xml.addAttribute("xlink:href", url);
out.xml.addAttribute("xlink:type", "simple");
out.xml.addAttribute("xlink:show", "embed");
out.xml.addAttribute("xlink:actuate", "onLoad");
out.xml.endElement(); //draw:image
//check for user edited wrap points
#if 0
if (ds.fEditedWrap()) {
QString points;
IMsoArray _v = ds.pWrapPolygonVertices_complex();
if (_v.data.size()) {
//_v.data is an array of POINTs, MS-ODRAW, page 89
QByteArray a, a2;
int* p;
for (int i = 0, offset = 0; i < _v.nElems; i++, offset += _v.cbElem) {
// x coordinate of this point
a = _v.data.mid(offset, _v.cbElem);
a2 = a.mid(0, _v.cbElem / 2);
p = (int*) a2.data();
points.append(QString::number(twipsToPt(*p), 'f'));
points.append(",");
// y coordinate of this point
a2 = a.mid(_v.cbElem / 2, _v.cbElem / 2);
p = (int*) a2.data();
points.append(QString::number(twipsToPt(*p), 'f'));
points.append(" ");
}
points.chop(1); //remove last space
}
out.xml.startElement("draw:contour-polygon");
out.xml.addAttribute("draw:points", points);
out.xml.endElement(); //draw:contour-polygon
}
#endif
out.xml.endElement(); //draw:frame
return;
}
void WordsGraphicsHandler::processLineShape(const MSO::OfficeArtSpContainer& o, DrawingWriter& out)
{
kDebug(30513) ;
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
const MSO::OfficeArtDggContainer *dgg = 0;
#ifdef USE_OFFICEARTDGG_CONTAINER
dgg = &m_officeArtDggContainer;
#endif
DrawStyle ds(dgg, 0, &o);
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
//TODO: maybe wrapping related attributes have to be set
//NOTE: also the dxWidthHR propertie may store the width information
float width = ds.pctHR() / 10.0;
QString hrAlign;
QString xPos = QString::number(0.0f).append("in");
const float base_width = 6.1378f;
switch (ds.alignHR()) {
case hAlignLeft:
hrAlign = QString("left");
xPos = QString::number(0.0f).append("in");
break;
case hAlignCenter:
hrAlign = QString("center");
xPos = QString::number((base_width / 2.0) - ((width * base_width) / 200.0), 'f').append("in");
break;
case hAlignRight:
hrAlign = QString("right");
xPos = QString::number(base_width - (width * base_width) / 100.0, 'f').append("in");
break;
}
//process the content of HR specific properties
style.addProperty("draw:textarea-horizontal-align", hrAlign);
style.addProperty("draw:textarea-vertical-align", "top");
if (ds.fNoshadeHR()) {
style.addProperty("draw:shadow", "hidden");
}
else {
style.addProperty("draw:shadow", "visible");
}
styleName = out.styles.insert(style);
//create a custom shape
out.xml.startElement("draw:custom-shape");
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
QString height = QString::number(ds.dxHeightHR() / 1440.0f, 'f').append("in");
out.xml.addAttribute("svg:height", height);
QString width_str = QString::number(width * base_width / 100.0f, 'f').append("in");
out.xml.addAttribute("svg:width", width_str);
out.xml.addAttribute("svg:x", xPos);
//--------------------
out.xml.startElement("draw:enhanced-geometry");
out.xml.addAttribute("svg:viewBox", "0 0 21600 21600");
out.xml.addAttribute("draw:type", "rectangle");
out.xml.addAttribute("draw:enhanced-path", "M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N");
out.xml.endElement(); //enhanced-geometry
out.xml.endElement(); //custom-shape
}
void WordsGraphicsHandler::insertEmptyInlineFrame(DrawingWriter& out)
{
if (m_objectType != Inline) return;
QString styleName;
KoGenStyle style(KoGenStyle::GraphicAutoStyle, "graphic");
style.setAutoStyleInStylesDotXml(out.stylesxml);
DrawStyle ds;
DrawClient drawclient(this);
ODrawToOdf odrawtoodf(drawclient);
odrawtoodf.defineGraphicProperties(style, ds, out.styles);
definePositionAttributes(style, ds);
defineWrappingAttributes(style, ds);
styleName = out.styles.insert(style);
out.xml.startElement("draw:frame");
out.xml.addAttribute("draw:style-name", styleName);
setAnchorTypeAttribute(out);
setZIndexAttribute(out);
double hscale = m_picf->mx / 1000.0;
double vscale = m_picf->my / 1000.0;
out.xml.addAttributePt("svg:width", twipsToPt(m_picf->dxaGoal) * hscale);
out.xml.addAttributePt("svg:height", twipsToPt(m_picf->dyaGoal) * vscale);
out.xml.endElement(); //draw:frame
}
diff --git a/interfaces/KoGenericRegistry.h b/interfaces/KoGenericRegistry.h
index 390c1afa45b..ddbe4ec3897 100644
--- a/interfaces/KoGenericRegistry.h
+++ b/interfaces/KoGenericRegistry.h
@@ -1,158 +1,157 @@
/* This file is part of the KDE project
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KO_GENERIC_REGISTRY_H_
#define _KO_GENERIC_REGISTRY_H_
-#include <kdebug.h>
#include <QList>
#include <QString>
#include <QHash>
/**
* Base class for registry objects.
*
* Registered objects are owned by the registry.
*
* Items are mapped by QString as a unique Id.
*
* Exemple of use:
* @code
* class KoMyClassRegistry : public KoGenericRegistry<MyClass*> {
* public:
* static KoMyClassRegistry * instance();
* private:
* static KoMyClassRegistry* s_instance;
* };
*
* KoMyClassRegistry *KoMyClassRegistry::s_instance = 0;
* KoMyClassRegistry * KoMyClassRegistry::instance()
* {
* if(s_instance == 0)
* {
* s_instance = new KoMyClassRegistry;
* }
* return s_instance;
* }
*
* @endcode
*/
template<typename T>
class KoGenericRegistry
{
public:
KoGenericRegistry() { }
virtual ~KoGenericRegistry() { m_hash.clear(); }
public:
/**
* Add an object to the registry. If it is a QObject, make sure it isn't in the
* QObject ownership hierarchy, since the registry itself is responsbile for
* deleting it.
*
* @param item the item to add (NOTE: T must have an QString id() const function)
*/
void add(T item) {
Q_ASSERT( item );
QString id = item->id();
if(m_hash.contains(id)) {
m_doubleEntries << value(id);
remove(id);
}
m_hash.insert(id, item);
}
/**
* add an object to the registry
* @param id the id of the object
* @param item the item to add
*/
void add(const QString &id, T item) {
Q_ASSERT( item );
if(m_hash.contains(id)) {
m_doubleEntries << value(id);
remove(id);
}
m_hash.insert(id, item);
}
/**
* This function removes an item from the registry
*/
void remove(const QString &id) {
m_hash.remove(id);
}
/**
* Retrieve the object from the registry based on the unique
* identifier string.
*
* @param id the id
*/
T get(const QString& id) const {
return value(id);
}
/**
* @return if there is an object stored in the registry identified
* by the id.
* @param id the unique identifier string
*/
bool contains(const QString &id) const {
return m_hash.contains(id);
}
/**
* Retrieve the object from the registry based on the unique identifier string
* @param id the id
*/
const T value(const QString &id) const {
return m_hash.value(id);
}
/**
* @return a list of all keys
*/
QList<QString> keys() const {
return m_hash.keys();
}
int count() const {
return m_hash.count();
}
QList<T> values() const {
return m_hash.values();
}
QList<T> doubleEntries() const {
return m_doubleEntries;
}
private:
QList<T> m_doubleEntries;
private:
QHash<QString, T> m_hash;
};
#endif
diff --git a/karbon/plugins/stencilbox/karbon_stencilbox.desktop b/karbon/plugins/stencilbox/karbon_stencilbox.desktop
index 2424ca6c26b..fc0984c9633 100644
--- a/karbon/plugins/stencilbox/karbon_stencilbox.desktop
+++ b/karbon/plugins/stencilbox/karbon_stencilbox.desktop
@@ -1,15 +1,16 @@
[Desktop Entry]
Name=Karbon Stencil Box plugin
Name[ca]=Connector del quadre de patrons del Karbon
Name[fi]=Karbonin sapluunalaatikkoliitännäinen
Name[nl]=Plug-in voor Karbon Stencil Box
+Name[pl]=Wtyczka szablonów dla Karbon
Name[pt]='Plugin' de Área de Formas do Karbon
Name[pt_BR]=Plugin de caixa de estêncil do Karbon
Name[sv]=Karbon insticksprogram med stencilruta
Name[uk]=Додаток панелі трафаретів для Karbon
Name[x-test]=xxKarbon Stencil Box pluginxx
X-KDE-ServiceTypes=Calligra/Dock
Type=Service
X-KDE-PluginInfo-Name=karbon_stencilbox
X-KDE-Library=karbon_stencilbox
X-Karbon-Version=28
diff --git a/kexi/formeditor/factories/kexiforms_standardwidgetsplugin.desktop b/kexi/formeditor/factories/kexiforms_standardwidgetsplugin.desktop
index 5fa73a0892f..da046fa99c7 100644
--- a/kexi/formeditor/factories/kexiforms_standardwidgetsplugin.desktop
+++ b/kexi/formeditor/factories/kexiforms_standardwidgetsplugin.desktop
@@ -1,33 +1,37 @@
[Desktop Entry]
Name=Standard Widgets
+Name[ca]=Estris estàndards
Name[fi]=Vakioelementit
Name[nl]=Standaardwidgets
+Name[pl]=Standardowe elementy interfejsu
Name[pt]=Elementos-Padrão
Name[pt_BR]=Elementos padrão
Name[sv]=Standardkomponenter
Name[uk]=Стандартні віджети
Name[x-test]=xxStandard Widgetsxx
Comment=Kexi plugin providing standard form widgets
+Comment[ca]=Connecto del Kexi que proporciona estris estàndard de formularis
Comment[nl]=Kexi-plug-in levert standaard formulierwidgets
+Comment[pl]=Wtyczka Kexi dostarczająca standarowe formularze
Comment[pt]='Plugin' do Kexi que oferece elementos gráficos de formulários
Comment[pt_BR]=Plugin do Kexi que oferece elementos gráficos de formulários
Comment[sv]=Kexi insticksprogram som tillhandahåller standardkomponenter för formulär
Comment[uk]=Додаток до Kexi, що забезпечує роботу форми стандартних віджетів
Comment[x-test]=xxKexi plugin providing standard form widgetsxx
Type=Service
Icon=form
Encoding=UTF-8
X-KDE-Library=kexiforms_standardwidgetsplugin
X-KDE-ServiceTypes=Kexi/FormWidgets
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.form.widgets.standard
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-FormWidgetsFactoryGroup=
diff --git a/kexi/formeditor/widgetfactory.desktop b/kexi/formeditor/widgetfactory.desktop
index fff6e87b320..ed99ccd0550 100644
--- a/kexi/formeditor/widgetfactory.desktop
+++ b/kexi/formeditor/widgetfactory.desktop
@@ -1,61 +1,61 @@
[Desktop Entry]
Type=ServiceType
X-KDE-ServiceType=Kexi/WidgetFactory
Comment=Widget Factory Base
Comment[bs]=Baza fabrike dodataka
Comment[ca]=Factoria base d'estris
Comment[ca@valencia]=Factoria base d'estris
Comment[cy]=Bâs Ffatri Celfigion
Comment[da]=Widget Factory Base
Comment[de]=Basis für die Erstellung von Bedienelementen
Comment[el]=Βάση μηχανής γραφικών συστατικών
Comment[en_GB]=Widget Factory Base
Comment[eo]=Fenestraĵfabrikbazo
Comment[es]=Fábrica base de elementos gráficos
Comment[et]=Vidinate põhibaas
Comment[eu]=Trepeta-faktoriaren oinarria
Comment[fa]=پایه کارخانه عنصر
Comment[fi]=Käyttöliittymäelementtien pohja
Comment[fr]=Base de création de composants graphiques
Comment[fy]=Widget Factory Base
Comment[gl]=Fábrica base de trebellos
Comment[he]=בסיס למפעל פריטים
Comment[hi]=विजेट फ़ैक्टरी आधार
Comment[hne]=विजेट फैक्टरी आधार
Comment[hr]=Baza widget tvornice
Comment[hu]=Widgetkészítő-alap
Comment[is]=Hluta smiðjugrunnur
Comment[it]=Fabbrica di oggetti
Comment[ja]=ウィジェットファクトリーベース
Comment[kk]=Виджеттер фабрикасының негізі
Comment[ko]=위젯 팩토리 기반
Comment[lv]=Logdaļas fabrikas bāze
Comment[ms]=Pangkalan Kilang Widget
Comment[nb]=Base for elementfabrikk
Comment[nds]=Basis för't Opstellen vun Stüerelementen
Comment[ne]=विजेट फ्याक्ट्री आधार
Comment[nl]=Widget Factory Base
-Comment[pl]=Podstawowa fabryka widżetów
+Comment[pl]=Podstawowa fabryka elementów interfejsu
Comment[pt]=Fábrica de Elementos
Comment[pt_BR]=Fábrica de elementos
Comment[ru]=Базовое приложение, содержащее офисный элемент управления
Comment[se]=Áhtafabrihka vuođđu
Comment[sk]=Základ pre vytváracie rozhranie prvkov
Comment[sl]=Tovarna gradnikov
Comment[sv]=Bas för att skapa grafiska komponenter
Comment[ta]=சாளர தொழிற்சாலை தளம்
Comment[tr]=Widget Factory Base
Comment[uk]=База фабрики віджетів
Comment[wa]=Båze del fabrike d' ahesses
Comment[x-test]=xxWidget Factory Basexx
Comment[zh_CN]=基础组件工厂
Comment[zh_TW]=視窗元件工廠基地
[PropertyDef::X-KFormDesigner-FactoryGroup]
Type=QString
[PropertyDef::X-KFormDesigner-WidgetFactoryVersion]
Type=int
[PropertyDef::X-KFormDesigner-XMLGUIFileName]
Type=QString
diff --git a/kexi/kexi.appdata.xml b/kexi/kexi.appdata.xml
index 091b727ee21..8232cce12d5 100644
--- a/kexi/kexi.appdata.xml
+++ b/kexi/kexi.appdata.xml
@@ -1,275 +1,285 @@
<?xml version="1.0" encoding="utf-8"?>
<component type="desktop">
<id>kexi.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LGPL-2.0+</project_license>
<name>Kexi</name>
<name xml:lang="ast">Kexi</name>
<name xml:lang="bs">Kexi</name>
<name xml:lang="ca">Kexi</name>
<name xml:lang="cs">Kexi</name>
<name xml:lang="da">Kexi</name>
<name xml:lang="de">Kexi</name>
<name xml:lang="en-GB">Kexi</name>
<name xml:lang="es">Kexi</name>
<name xml:lang="et">Kexi</name>
<name xml:lang="fi">Kexi</name>
<name xml:lang="fr">Kexi</name>
<name xml:lang="gl">Kexi</name>
<name xml:lang="ia">Kexi</name>
<name xml:lang="it">Kexi</name>
<name xml:lang="ja">Kexi</name>
<name xml:lang="nb">Kexi</name>
<name xml:lang="nl">Kexi</name>
<name xml:lang="pl">Kexi</name>
<name xml:lang="pt">Kexi</name>
<name xml:lang="pt-BR">Kexi</name>
<name xml:lang="sk">Kexi</name>
<name xml:lang="sv">Kexi</name>
<name xml:lang="uk">Kexi</name>
<name xml:lang="x-test">xxKexixx</name>
<name xml:lang="zh-CN">Kexi</name>
<summary>Visual database apps builder</summary>
+ <summary xml:lang="ca">Constructor visual d'aplicacions de bases de dades</summary>
+ <summary xml:lang="fi">Visuaalinen tietokantasovellusten luonti</summary>
<summary xml:lang="nl">Visuele database van bouwer van apps</summary>
<summary xml:lang="pt">Construtor visual de aplicações de bases de dados</summary>
<summary xml:lang="pt-BR">Construtor visual de aplicativos de banco de dados</summary>
<summary xml:lang="sv">Visuellt byggverktyg för databasprogram</summary>
<summary xml:lang="uk">Програма для побудови візуальних обробників баз даних</summary>
<summary xml:lang="x-test">xxVisual database apps builderxx</summary>
<description>
<p>
Kexi is a visual database applications builder. It can be used for designing database applications,
inserting and editing data, performing queries, and processing data. Forms can be created
to provide a custom interface to your data. All database objects - tables, queries, forms,
reports, etc. - are stored in the database, making it easy to share data and design.
</p>
+ <p xml:lang="ca">El Kexi és un constructor visual de base de dades. Es pot utilitzar per dissenyar aplicacions de bases de dades, inserció i edició de dades, execució de consultes, i procés de dades. Es poden generar formularis per proporcionar una interfície personalitzada a les dades. Tots els objectes de les bases de dades -taules, consultes, formularis, informes, etc.- s'emmagatzemen en la base de dades, facilitant compartir les dades i el disseny.</p>
+ <p xml:lang="fi">Kexi on ohjelma tietokantasovellusten luomiseen visuaalisesti. Se on osa Calligra-ohjelmistoa. Sen avulla voi suunnitella tietokantasovelluksia, lisätä ja muokata tietoja, suorittaa kyselyjä ja käsitellä tietoja. Voit luoda lomakkeita mukauttaaksesi tietojesi käyttöliittymää. Kaikki tietokantaoliot – taulukot, kyselyt, lomakkeet, raportit jne. – tallennetaan tietokantaan, joten tiedon jako ja suunnittelu on helppoa.</p>
<p xml:lang="nl">Kexi is een bouwer van toepassingen met een database op een visuele manier. Het kan gebruikt worden voor het ontwerpen van toepassingen met een database, gegevens invoeren en bewerken, zoekopdrachten uitvoeren en gegevens verwerken. Formulieren kunnen gemaakt worden om een aangepast interface naar uw gegevens te bieden. Alle database-objecten - tabellen, zoekopdrachten, formulieren, rapporten, etc. - worden opgeslagen in de database, waarmee het gemakkelijk wordt gegevens en ontwerp te delen.</p>
<p xml:lang="pt">O Kexi é um criador de bases de dados visuais, que pode ser usado para desenhar aplicações de bases de dados, inserir e editar dados, executar pesquisas e processar os dados. Poderá criar formulários para oferecer uma interface personalizada para os seus dados. Todos os objectos da base de dados - tabelas, pesquisas, formulários, relatórios, etc. - são guardados na base de dados, tornando simples a partilha e o desenho dos dados.</p>
<p xml:lang="pt-BR">Kexi é um construtor de aplicativos visuais de banco de dados. Pode ser usado para desenhar aplicativos de banco de dados, inserir e editar dados, executar pesquisas e processar dados. Podem ser criados formulários para oferecer uma interface personalizada para seus dados. Todos os objetos do banco de dados - tabelas, pesquisas, formulários, relatórios, etc. - são armazenados no próprio banco de dados, tornando simples o compartilhamento de dados e o visual.</p>
<p xml:lang="sv">Kexi är ett visuell byggverktyg för databasprogram, en del av Calligra-sviten. Den kan användas för att skapa databasprogram, infoga och redigera data, ställa frågor och behandla data. Formulär kan skapas för att tillhandahålla ett anpassat gränssnitt för data. Alla databasobjekt (tabeller, frågor, formulär, rapporter, etc.) lagras i databasen, vilket gör det enkelt att dela data och konstruktion.</p>
<p xml:lang="uk">Kexi — програма для створення обробників баз даних у візуальному режимі. Цією програмою можна скористатися для створень мініпрограм для роботи з базами даних, додавання та редагування даних, виконання запитів та обробки даних. За допомогою створених програмою форм ви можете отримати потрібний вам режим доступу до ваших даних. Всі об’єкти баз даних — таблиці, запити, форми, звіти тощо, — зберігаються у самій базі даних, що спрощує поширення даних та представлення даних.</p>
<p xml:lang="x-test">xxKexi is a visual database applications builder. It can be used for designing database applications, inserting and editing data, performing queries, and processing data. Forms can be created to provide a custom interface to your data. All database objects - tables, queries, forms, reports, etc. - are stored in the database, making it easy to share data and design.xx</p>
<p>Features:</p>
<p xml:lang="bs">Osobine:</p>
<p xml:lang="ca">Característiques:</p>
<p xml:lang="cs">Vlastnosti:</p>
<p xml:lang="de">Funktionen:</p>
<p xml:lang="en-GB">Features:</p>
<p xml:lang="es">Características:</p>
<p xml:lang="et">Omadused:</p>
<p xml:lang="fi">Ominaisuuksia:</p>
<p xml:lang="fr">Fonctionnalités :</p>
<p xml:lang="gl">Funcionalidades:</p>
<p xml:lang="ia">Characteristicas</p>
<p xml:lang="it">Funzionalità:</p>
<p xml:lang="ja">機能:</p>
<p xml:lang="nb">Funksjoner:</p>
<p xml:lang="nl">Mogelijkheden:</p>
<p xml:lang="pl">Możliwości:</p>
<p xml:lang="pt">Funcionalidades:</p>
<p xml:lang="pt-BR">Funcionalidades:</p>
<p xml:lang="sk">Funkcie:</p>
<p xml:lang="sv">Funktioner:</p>
<p xml:lang="uk">Можливості:</p>
<p xml:lang="x-test">xxFeatures:xx</p>
<p xml:lang="zh-CN">功能:</p>
<ul>
<li>Full visual designers for tables, queries, forms and reports</li>
<li xml:lang="bs">Potpuni vizualni dizajneri za tablice , upite , obrasce i izvještaje</li>
<li xml:lang="ca">Dissenyadors visuals complets per taules, consultes, formularis i informes</li>
<li xml:lang="en-GB">Full visual designers for tables, queries, forms and reports</li>
<li xml:lang="es">Herramientas de diseño completamente visuales para las tablas, consultas, formularios e informes.</li>
<li xml:lang="et">Tabelite, päringute, vormide ja aruannete täielikult visuaalne kujundamine</li>
<li xml:lang="fi">Täysin visuaalinen taulukoiden, kyselyjen, lomakkeiden ja raporttien suunnittelu</li>
<li xml:lang="fr">Conception complètement visuelle pour les tables, requêtes, formulaires et rapports</li>
<li xml:lang="it">Progettazione completamente visuale di tabelle, query e report</li>
<li xml:lang="ja">テーブル、クエリー、フォームおよびレポートをすべて視覚的にでデザインできます</li>
<li xml:lang="nb">Fullstendig visuell utforming for tabeller, spøøringer, skjemaer og rapporter</li>
<li xml:lang="nl">Volledige visueel ontwerpen van tabellen, zoekopdrachten, formulieren en rapporten</li>
<li xml:lang="pl">W pełni wizualna możliwość tworzenia tabel, zapytań, formularzy i raportów</li>
<li xml:lang="pt">Desenhadores visuais completos para tabelas, pesquisas, formulários e relatórios</li>
<li xml:lang="pt-BR">Desenhos visuais completos para tabelas, pesquisas, formulários e relatórios</li>
<li xml:lang="sk">Plne vizuálny návrh pre tabuľky, dotazy, formuláre a správy</li>
<li xml:lang="sv">Fullständigt visuell konstruktion av tabeller, frågor, formulär och rapporter</li>
<li xml:lang="uk">Повноцінне візуальне середовище для створення таблиць, запитів, форм та звітів.</li>
<li xml:lang="x-test">xxFull visual designers for tables, queries, forms and reportsxx</li>
<li>Running queries, support for parametrized queries</li>
<li xml:lang="bs">Potrebno upite , podršku za parametrizovan upite</li>
<li xml:lang="ca">Execució de consultes, implementació de consultes paramètriques</li>
<li xml:lang="de">Ausführung von Abfragen, Unterstützung für parametrisierte Abfragen</li>
<li xml:lang="en-GB">Running queries, support for parametrised queries</li>
<li xml:lang="es">Ejecución de consultas, soporte de consultas parametrizadas</li>
<li xml:lang="et">Päringute sooritamine, parameetritega päringute toetamine</li>
<li xml:lang="fi">Kyselyjen suoritus ja parametroitavien kyselyjen tuki</li>
<li xml:lang="fr">Exécution de requêtes, prise en charge des requêtes paramétrées</li>
<li xml:lang="it">Esecuzione di query, supporto per le query parametriche</li>
<li xml:lang="ja">クエリーの実行、パラメータ化クエリーのサポート</li>
<li xml:lang="nb">Kjøre spørriner, støtte for parametriserte spørringer</li>
<li xml:lang="nl">Zoekopdrachten uitvoeren, ondersteuning voor zoekopdrachten met parameters</li>
<li xml:lang="pl">Wykonywanie zapytań, obsługa dla zapytań parametrycznych</li>
<li xml:lang="pt">Execução de pesquisas, suporte para pesquisas parametrizadas</li>
<li xml:lang="pt-BR">Execução de pesquisas, suporte para pesquisas parametrizadas</li>
<li xml:lang="sk">Spúšťanie dotazov, podpora pre parametrizované dotazy</li>
<li xml:lang="sv">Ställa frågor, stöd för parameterbaserade frågor</li>
<li xml:lang="uk">Виконання запитів, підтримка параметризованих запитів.</li>
<li xml:lang="x-test">xxRunning queries, support for parametrized queriesxx</li>
<li>Supports SQLite, MySQL, PostgreSQL, xBase, and Sybase/MS SQL Server databases</li>
+ <li xml:lang="ca">Permet bases de dades SQLite, MySQL, PostgreSQL, xBase i Sybase/MS SQL Server</li>
+ <li xml:lang="fi">SQLite-, MySQL-, PostgreSQL-, xBase- ja Sybase/MS SQL Server -tietokantojen tuki</li>
<li xml:lang="nl">Ondersteunt databases SQLite, MySQL, PostgreSQL, xBase en Sybase/MS SQL Server</li>
<li xml:lang="pt">Suporta bases de dados SQLite, MySQL, PostgreSQL, xBase e Sybase/MS SQL Server</li>
<li xml:lang="pt-BR">Suporte aos banco de dados SQLite, MySQL, PostgreSQL, xBase e Sybase/MS SQL Server</li>
<li xml:lang="sv">Stöder databaserna SQLite, MySQL, PostgreSQL, xBase och Sybase/MS SQL Server</li>
<li xml:lang="uk">Підтримка баз даних SQLite, MySQL, PostgreSQL, xBase та Sybase/MS SQL.</li>
<li xml:lang="x-test">xxSupports SQLite, MySQL, PostgreSQL, xBase, and Sybase/MS SQL Server databasesxx</li>
<li>The only multiplatform visual tool that can easily import data from MS Access databases</li>
+ <li xml:lang="ca">L'única eina visual multiplataforma que pot importar fàcilment dades des de les bases de dades MS Access</li>
+ <li xml:lang="fi">Ainoa useampialustainen visuaalinen työkalu, jolla voi helposti tuoda tietoja MS Access -tietokannoista</li>
<li xml:lang="nl">Het enige multiplatform visuele hulpmiddel die gemakkelijk gegevens kan importeren uit MS Access databases</li>
<li xml:lang="pt">A única ferramenta gráfica multi-plataforma que consegue importar facilmente bases de dados MS Access</li>
<li xml:lang="pt-BR">Única ferramenta visual e multiplataforma que consegue importar facilmente um banco de dados do MS Access</li>
<li xml:lang="sv">Det enda visuella verktyget för flera plattformar som enkelt kan importera data från MS Access-databaser</li>
<li xml:lang="uk">Єдиний візуальний багатоплатформовий інструмент, здатний без проблем імпортувати дані з баз даних MS Access.</li>
<li xml:lang="x-test">xxThe only multiplatform visual tool that can easily import data from MS Access databasesxx</li>
<li>Support for query design in dedicated SQL view</li>
+ <li xml:lang="ca">Permet el disseny de consultes en vistes SQL dedicades</li>
+ <li xml:lang="fi">Kyselyn suunnittelun tuki SQL-näkymässä</li>
<li xml:lang="nl">Ondersteuning voor ontwerp van zoekopdrachten in een gerichte SQL-weergave</li>
<li xml:lang="pt">Suporta o desenho de pesquisas numa área de SQL dedicada</li>
<li xml:lang="pt-BR">Suporte para desenho de pesquisas em uma área de SQL dedicada</li>
<li xml:lang="sv">Stöd för frågekonstruktion i en dedicerad SQL-vy</li>
<li xml:lang="uk">Підтримка створення запитів за допомогою спеціальної панелі SQL.</li>
<li xml:lang="x-test">xxSupport for query design in dedicated SQL viewxx</li>
<li>Designing and displaying relational data using combo boxes (lookup columns)</li>
<li xml:lang="bs">Izrada i prikazivanje relacijskih podataka pomoću combo kutije ( pregledavanje kolone)</li>
<li xml:lang="ca">Disseny i visualització de dades relacionals usant quadres combinats (columnes de cerca)</li>
<li xml:lang="en-GB">Designing and displaying relational data using combo boxes (lookup columns)</li>
<li xml:lang="es">Diseño y presentación de datos relacionales utilizando listas desplegables (columnas de búsqueda)</li>
<li xml:lang="et">Relatsiooniliste andmete kujundamine ja kuvamine liitkastide abil (päringuveerud)</li>
<li xml:lang="fi">Relaatiotiedon suunnittelu ja näyttö yhdistelmäkenttiä käyttäen (hakusarakkeet)</li>
<li xml:lang="fr">Conception et affichage de données relationnelles à l'aide de listes déroulantes (colonnes de consultation)</li>
<li xml:lang="it">Progettazione e visualizzazione dei dati relazionali utilizzando caselle combinate (colonne di ricerca)</li>
<li xml:lang="ja">コンボボックスを使用した関係データのデザインと表示(参照列)</li>
<li xml:lang="nb">Utforming og visning av relasjonsdata vhja kombo-bokser (oppslagskolonner)</li>
<li xml:lang="nl">Ontwerpen en tonen van relationele gegevens met gebruik van keuzelijsten (kolommen voor opzoeken)</li>
<li xml:lang="pl">Opracowywanie i wyświetlanie danych relacyjnych przy użyciu pól rozwijanych (kolumn wyszukiwania)</li>
<li xml:lang="pt">Desenho e apresentação de dados relacionais com listas (colunas de pesquisa)</li>
<li xml:lang="pt-BR">Desenho e apresentação de dados relacionais usando listas (colunas de pesquisa)</li>
<li xml:lang="sk">Návrh a zobrazenie relačných dát pomocou combo boxov (lookup stĺpce)</li>
<li xml:lang="sv">Konstruktion och visning av relationsdata med användning av kombinationsrutor (uppslagning av kolumner)</li>
<li xml:lang="uk">Створення та показ пов’язаних даних за допомогою спадних списків (стовпчики пошуку).</li>
<li xml:lang="x-test">xxDesigning and displaying relational data using combo boxes (lookup columns)xx</li>
<li>Direct data entry or import using CSV format</li>
<li xml:lang="bs">Unos podataka direktno ili uvoz pomoću CSV formata</li>
<li xml:lang="ca">Entrada directa de dades o importació usant el format CSV</li>
<li xml:lang="de">Direkte Dateneingabe oder Import aus dem CSV-Format</li>
<li xml:lang="en-GB">Direct data entry or import using CSV format</li>
<li xml:lang="es">Entrada directa de datos o importación utilizando el formato CSV</li>
<li xml:lang="et">Otsene andmete sisestamine või importimine CSV-vormingus</li>
<li xml:lang="fi">Suora tiedon syöttö tai tuonti CSV-muodossa</li>
<li xml:lang="fr">Saisie directe de données ou import utilisant le format CSV</li>
<li xml:lang="it">Inserimento diretto dei dati o importazione utilizzando il formato CSV</li>
<li xml:lang="ja">CSV を使用したダイレクトデータの入力もしくはインポート</li>
<li xml:lang="nb">Direkte datainnskriving eller import med CSV-format</li>
<li xml:lang="nl">Directe invoer van gegevens of importeren met het CSV-format</li>
<li xml:lang="pl">Bezpośrednie wprowadzanie danych lub importowanie ich z postaci CSV</li>
<li xml:lang="pt">Introdução ou importação directa de dados com o formato CSV</li>
<li xml:lang="pt-BR">Entrada ou importação direta de dados usando o formato CSV</li>
<li xml:lang="sk">Priamy vstup údajov alebo import pomocou formátu CSV</li>
<li xml:lang="sv">Direkt datainmatning eller import med CSV-format</li>
<li xml:lang="uk">Безпосереднє введення даних або імпортування даних у форматі CSV.</li>
<li xml:lang="x-test">xxDirect data entry or import using CSV formatxx</li>
<li>Locked down User Mode, allowing you to switch off all commands related to editing your project's design</li>
<li xml:lang="bs">Zatvoreni korisnički režim, omogućavajući vam da isključite sve naredbe koje se odnose na uređivanje dizajna vašeg projekta</li>
<li xml:lang="ca">Mode reduït d'usuari, que permet deshabilitar totes les ordres relacionades amb l'edició del disseny del projecte</li>
<li xml:lang="en-GB">Locked down User Mode, allowing you to switch off all commands related to editing your project's design</li>
<li xml:lang="es">Modo de usuario limitado, que permite desactivar todas las ordenes relacionadas con la edición del diseño de su proyecto</li>
<li xml:lang="et">Piiratud õigustega kasutajarežiim, mis võimaldab lülitada välja kõik projekti kujunduse muutmisega seotud käsud</li>
<li xml:lang="fi">Lukittu käyttäjätila, jossa voit poistaa käytöstä projektin suunnitteluun liittyvät komennot</li>
<li xml:lang="fr">Mode utilisateur verrouillé, vous permettant de désactiver toutes les commandes liées à la modification de la conception de votre projet</li>
<li xml:lang="it">Modalità utente bloccata, che ti consente di disattivare tutti i comandi in grado di modificare la struttura del tuo progetto.</li>
<li xml:lang="ja">プロジェクトデザインの編集スイッチをオフにする事でユーザモードへのロックダウンが可能</li>
<li xml:lang="nb">Låst brukermodus, slik at du kan slå av alle kommandoer som gjelder endring av prosjektets utforming</li>
<li xml:lang="nl">Vergrendelde gebruikersodus, waarmee u alle commando's uitschakelt die zijn gerelateerd aan bewerken en ontwerpen van uw project</li>
<li xml:lang="pl">Zamknięty tryb użytkownika, umożliwiający wyłączenie wszystkich poleceń związanych z edytowaniem twojego projektu</li>
<li xml:lang="pt">Modo reduzido de utilizador, permitindo-lhe desligar todos os comandos relacionados com a edição do desenho do seu projecto</li>
<li xml:lang="pt-BR">Modo de usuário reduzido, permitindo-lhe desligar todos os comandos relacionados à edição do visual do seu projeto</li>
<li xml:lang="sk">Uzamknutý používateľský režim, umožňujúci vám vypnúť všetky príkazy súvisiace s úpravou dizajnu vášho projektu</li>
<li xml:lang="sv">Låsbart användarläge, som låter dig stänga av alla kommandon relaterade till redigering av projektets konstruktion</li>
<li xml:lang="uk">Заблокований режим користувача, у якому можна вимкнути усі пов’язані з командами редагування компонування вашого проекту команди.</li>
<li xml:lang="x-test">xxLocked down User Mode, allowing you to switch off all commands related to editing your project's designxx</li>
<li>Scripting using JavaScript, Python or Ruby programming languages (experimental)</li>
<li xml:lang="bs">Scripting koristeći JavaScript, Python ili Ruby programskim jezicima (eksperimentalno)</li>
<li xml:lang="ca">Crear scripts usant els llenguatges de programació JavaScript, Python o Ruby (experimental)</li>
<li xml:lang="de">Unterstützung für Skripte in den Programmsprachen JavaScript, Python oder Ruby (experimentell)</li>
<li xml:lang="en-GB">Scripting using JavaScript, Python or Ruby programming languages (experimental)</li>
<li xml:lang="es">Programación de scripts escritos en los lenguajes de programación JavaScript, Python o Ruby (experimental)</li>
<li xml:lang="et">Skriptimisvõimalused JavaScripti, Pythoni või Ruby programmeerimiskeeles (eksperimentaalne)</li>
<li xml:lang="fi">Skriptaus JavaScript-, Python- tai Ruby-ohjelmointikielillä (kokeellinen)</li>
<li xml:lang="fr">Pilotage par scripts utilisant les les langages JavaScript, Python ou Ruby (expérimental)</li>
<li xml:lang="it">Creazione script utilizzando i linguaggi di programmazione JavaScript, Python o Ruby (sperimentale)</li>
<li xml:lang="ja">JavaScript, Python や Ruby を使用してのスクリプティング(実験的)</li>
<li xml:lang="nb">Skripting ved bruk av JavaScript, Python eller Ruby programmeringsspråk (eksperimentelt)</li>
<li xml:lang="nl">Scripting met de programmeertalen JavaScript, Python of Ruby (experimenteel)</li>
<li xml:lang="pl">Skrypty wykorzystujące języki programowania JavaScript, Python lub Ruby (eksperymentalnie)</li>
<li xml:lang="pt">Programável através das linguagens JavaScript, Python ou Ruby (experimental)</li>
<li xml:lang="pt-BR">Criação de scripts usando as linguagens de programação JavaScript, Python ou Ruby (experimental)</li>
<li xml:lang="sk">Skriptovanie pomocou programovacích jazykov JavaScript, Python alebo Ruby (experimentálne)</li>
<li xml:lang="sv">Skript med användning av programspråken Javascript, Python eller Ruby (experimentellt)</li>
<li xml:lang="uk">Керування за допомогою скриптів мовами JavaScript, Python та Ruby (експериментальна можливість).</li>
<li xml:lang="x-test">xxScripting using JavaScript, Python or Ruby programming languages (experimental)xx</li>
</ul>
</description>
<url type="homepage">http://www.calligra.org/kexi/</url>
<url type="bugtracker">https://bugs.kde.org/enter_bug.cgi?format=guided&amp;product=kexi</url>
<url type="help">http://userbase.kde.org/Kexi/Handbook</url>
<screenshots>
<screenshot type="default">
<image>https://www.calligra.org/wp-content/uploads/2012/06/kexi-2.5-new-form-widgets.png</image>
</screenshot>
<screenshot>
<caption>Table View storing images</caption>
<caption xml:lang="bs">Tabela View čuvanje slika</caption>
<caption xml:lang="ca">Vista de taula emmagatzemant imatges</caption>
<caption xml:lang="de">Tabellenansicht mit Bildern</caption>
<caption xml:lang="en-GB">Table View storing images</caption>
<caption xml:lang="es">Una vista de tabla para almacenar imágenes</caption>
<caption xml:lang="et">Tabelivaates piltide salvestamise võimalus</caption>
<caption xml:lang="fi">Kuvien tallennus taulukkonäkymään</caption>
<caption xml:lang="fr">Affichage de tables stockant des images</caption>
<caption xml:lang="it">Vista tabella per archiviare immagini</caption>
<caption xml:lang="ja">イメージを格納するテーブルビュー</caption>
<caption xml:lang="nb">Tabellvisning som lagrer bilder</caption>
<caption xml:lang="nl">Tabelweergave bij opslaan van afbeeldingen</caption>
<caption xml:lang="pl">Przechowywanie obrazów w widoku tabeli</caption>
<caption xml:lang="pt">A área da tabela permite guardar as imagens</caption>
<caption xml:lang="pt-BR">Área da tabela armazenando imagens</caption>
<caption xml:lang="sk">Tabuľkový pohľad uloženia obrázkov</caption>
<caption xml:lang="sv">Tabellvy som lagrar bilder</caption>
<caption xml:lang="uk">Збереження зображень у перегляді таблиць.</caption>
<caption xml:lang="x-test">xxTable View storing imagesxx</caption>
<image>http://www.calligra.org/wp-content/uploads/2011/11/kexi-2.2-rc1-tableview-images.png</image>
</screenshot>
<screenshot>
<caption>Form Designer</caption>
<caption xml:lang="bs">Obrazac Designer</caption>
<caption xml:lang="ca">Dissenyador de formularis</caption>
<caption xml:lang="de">Formular-Designer</caption>
<caption xml:lang="en-GB">Form Designer</caption>
<caption xml:lang="es">Diseñador de formularios</caption>
<caption xml:lang="et">Vormikujundaja</caption>
<caption xml:lang="fi">Lomakkeiden suunnittelu</caption>
<caption xml:lang="fr">Concepteur de formulaires</caption>
<caption xml:lang="it">Progettazione moduli</caption>
<caption xml:lang="ja">フォームデザイナー</caption>
<caption xml:lang="nb">Skjemautformer</caption>
<caption xml:lang="nl">Ontwerper van formulieren</caption>
<caption xml:lang="pl">Opracowywanie formularzy</caption>
<caption xml:lang="pt">Desenhador de Formulários</caption>
<caption xml:lang="pt-BR">Ferramenta de desenho de formulários</caption>
<caption xml:lang="sk">Návrhár formulárov</caption>
<caption xml:lang="sv">Formulärkonstruktion</caption>
<caption xml:lang="uk">Засіб для розробки форм.</caption>
<caption xml:lang="x-test">xxForm Designerxx</caption>
<image>http://www.calligra.org/wp-content/uploads/2011/11/kexi-2.3-form.png</image>
</screenshot>
</screenshots>
<project_group>KDE</project_group>
<provides>
<binary>calligrakexi</binary>
</provides>
</component>
diff --git a/kexi/kexi.desktop b/kexi/kexi.desktop
index cbd2bd28845..e691a863354 100644
--- a/kexi/kexi.desktop
+++ b/kexi/kexi.desktop
@@ -1,87 +1,91 @@
[Desktop Entry]
Name=Kexi
Name[bg]=Kexi
Name[br]=Kexi
Name[bs]=Kexi
Name[ca]=Kexi
Name[ca@valencia]=Kexi
Name[cs]=Kexi
Name[cy]=Kexi
Name[da]=Kexi
Name[de]=Kexi
Name[el]=Kexi
Name[en_GB]=Kexi
Name[eo]=Kexi
Name[es]=Kexi
Name[et]=Kexi
Name[eu]=Kexi
Name[fi]=Kexi
Name[fr]=Kexi
Name[fy]=Kexi
Name[ga]=Kexi
Name[gl]=Kexi
Name[he]=Kexi
Name[hi]=केक्साई
Name[hne]=केक्साई
Name[hr]=Kexi
Name[hu]=Kexi
Name[is]=Kexi
Name[it]=Kexi
Name[ja]=Kexi
Name[kk]=Kexi
Name[ko]=Kexi
Name[lt]=Kexi
Name[lv]=Kexi
Name[ms]=Kexi
Name[nb]=Kexi
Name[nds]=Kexi
Name[ne]=केक्सी
Name[nl]=Kexi
Name[pl]=Kexi
Name[pt]=Kexi
Name[pt_BR]=Kexi
Name[ro]=Kexi
Name[ru]=Kexi
Name[se]=Kexi
Name[sk]=Kexi
Name[sl]=Kexi
Name[sv]=Kexi
Name[ta]=கெக்ஸி
Name[tg]=Kexi
Name[tr]=Kexi
Name[ug]=Kexi
Name[uk]=Kexi
Name[uz]=Kexi
Name[uz@cyrillic]=Kexi
Name[wa]=Kexi
Name[x-test]=xxKexixx
Name[zh_CN]=Kexi
Name[zh_TW]=資料庫_Kexi
Type=Application
Exec=kexi
Icon=calligrakexi
MimeType=application/x-kexiproject-sqlite;application/x-kexiproject-shortcut;application/x-kexiproject-sqlite3;application/x-sqlite3;application/x-kexi-connectiondata;text/csv;text/tab-separated-values;application/vnd.oasis.opendocument.spreadsheet;application/vnd.oasis.opendocument.text;application/x-msaccess;application/vnd.oasis.opendocument.database
Terminal=false
GenericName=Visual database apps builder
+GenericName[ca]=Constructor visual d'aplicacions de bases de dades
GenericName[de]=Visuelle Erstellung von Datenbankanwendungen
GenericName[nl]=Visuele database van bouwer van apps
+GenericName[pl]=Tworzenie wizualnych aplikacji baz danych
GenericName[pt]=Construtor visual de aplicações de bases de dados
GenericName[pt_BR]=Construtor visual de aplicativos de banco de dados
GenericName[sv]=Visuellt byggverktyg för databasprogram
GenericName[uk]=Програма для побудови візуальних обробників баз даних
GenericName[x-test]=xxVisual database apps builderxx
Comment=Visually develop database applications
+Comment[ca]=Desenvolupament visual d'aplicacions de bases de dades
Comment[de]=Visuelle Entwicklung von Datenbankanwendungen
Comment[fi]=Kehitä tietokantasovelluksia visuaalisesti
Comment[nl]=Databasetoepassingen visueel ontwerpen
+Comment[pl]=Twórz wizualne aplikacje bazodanowe
Comment[pt]=Desenvolver de forma visual aplicações de bases de dados
Comment[pt_BR]=Desenvolve aplicativos visuais de bancos de dados
Comment[sv]=Utveckla databasprogram visuellt för skrivbordet
Comment[uk]=Візуальна розробка програм для роботи із базами даних
Comment[x-test]=xxVisually develop database applicationsxx
X-KDE-ServiceTypes=Calligra/Application
X-Calligra-DefaultMimeTypes=application/x-kexiproject-sqlite,application/x-kexiproject-shortcut,application/x-kexiproject-sqlite3,application/x-sqlite3,application/x-kexi-connectiondata,application/x-msaccess
X-DocPath=kexi/index.html
X-KDE-NativeMimeType=application/x-kexiproject-sqlite
X-KDE-NOTKoDocumentEmbeddable=true
Categories=Qt;KDE;Office;
diff --git a/kexi/plugins/forms/kexi_formplugin.desktop b/kexi/plugins/forms/kexi_formplugin.desktop
index b4edcc598f1..c99be9e8ee7 100644
--- a/kexi/plugins/forms/kexi_formplugin.desktop
+++ b/kexi/plugins/forms/kexi_formplugin.desktop
@@ -1,68 +1,70 @@
[Desktop Entry]
Name=Form
Name[bs]=Formular
Name[ca]=Formulari
Name[ca@valencia]=Formulari
Name[cs]=Formulář
Name[da]=Formular
Name[de]=Formular
Name[el]=Φόρμα
Name[en_GB]=Form
Name[eo]=Formo
Name[es]=Formulario
Name[et]=Vorm
Name[eu]=Inprimakia
Name[fi]=Lomake
Name[fr]=Formulaire
Name[gl]=Formulario
Name[hu]=Űrlap
Name[ia]=Formato
Name[it]=Modulo
Name[ja]=フォーム
Name[kk]=Пішін
Name[ko]=폼
Name[lt]=Forma
Name[mr]=फॉर्म
Name[nb]=Skjema
Name[nl]=Formulier
Name[pl]=Formularz
Name[pt]=Formulário
Name[pt_BR]=Formulário
Name[ru]=Форма
Name[sk]=Formulár
Name[sl]=Obrazec
Name[sv]=Formulär
Name[ug]=كۆزنەك
Name[uk]=Форма
Name[x-test]=xxFormxx
Name[zh_TW]=表單
Comment=Kexi plugin for handling forms
+Comment[ca]=Connector del Kexi per gestionar formularis
Comment[de]=Kexi-Modul für die Bearbeitung von Formularen
Comment[nl]=Kexi-plug-in voor behandeling van formulieren
+Comment[pl]=Wtyczka Kexi do obsługi formularzy
Comment[pt]='Plugin' do Kexi para lidar com formulários
Comment[pt_BR]=Plugin do Kexi para trabalhar com formulários
Comment[sv]=Kexi-insticksprogram för att hantera formulär
Comment[uk]=Додаток до Kexi для обробки форм
Comment[x-test]=xxKexi plugin for handling formsxx
Type=Service
Icon=form
Encoding=UTF-8
X-KDE-Library=kexi_formplugin
X-KDE-ServiceTypes=Kexi/Viewer,Kexi/Designer
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.form
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-GroupName=Forms
X-Kexi-TypeName=form
X-Kexi-ServiceTypesInUserMode=Kexi/Viewer
X-Kexi-VisibleInProjectNavigator=true
X-Kexi-SupportsDataExport=true
X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/forms/kexiforms_dbwidgetsplugin.desktop b/kexi/plugins/forms/kexiforms_dbwidgetsplugin.desktop
index 8d0753071fa..59d39be8ad3 100644
--- a/kexi/plugins/forms/kexiforms_dbwidgetsplugin.desktop
+++ b/kexi/plugins/forms/kexiforms_dbwidgetsplugin.desktop
@@ -1,33 +1,37 @@
[Desktop Entry]
Name=Database Widgets
+Name[ca]=Estris de base de dades
Name[fi]=Tietokantaelementit
Name[nl]=Databasewidgets
+Name[pl]=Baza danych elementów interfejsu
Name[pt]=Elementos de Bases de Dados
Name[pt_BR]=Elementos de banco de dados
Name[sv]=Databaskomponenter
Name[uk]=Віджети бази даних
Name[x-test]=xxDatabase Widgetsxx
Comment=Kexi plugin providing database form widgets
+Comment[ca]=Connecto del Kexi que proporciona estris de formularis de bases de dades
Comment[nl]=Kexi-plug-in levert formulierwidgets voor database
+Comment[pl]=Wtyczka Kexi dostarczająca formularze bazodanowe
Comment[pt]='Plugin' do Kexi que oferece elementos gráficos de formulários de bases de dados
Comment[pt_BR]=Plugin do Kexi que oferece elementos gráficos de formulários de bancos de dados
Comment[sv]=Kexi-insticksprogram som tillhandahåller komponenter för databasformulär
Comment[uk]=Додаток до Kexi, що забезпечує роботу віджетів форм бази даних
Comment[x-test]=xxKexi plugin providing database form widgetsxx
Type=Service
Icon=form
Encoding=UTF-8
X-KDE-Library=kexiforms_dbwidgetsplugin
X-KDE-ServiceTypes=Kexi/FormWidgets
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.form.widgets.db
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-FormWidgetsFactoryGroup=
diff --git a/kexi/plugins/forms/widgets/mapbrowser/kexiforms_mapwidgetplugin.desktop b/kexi/plugins/forms/widgets/mapbrowser/kexiforms_mapwidgetplugin.desktop
index a9567d3209e..9a42f3ced44 100644
--- a/kexi/plugins/forms/widgets/mapbrowser/kexiforms_mapwidgetplugin.desktop
+++ b/kexi/plugins/forms/widgets/mapbrowser/kexiforms_mapwidgetplugin.desktop
@@ -1,33 +1,37 @@
[Desktop Entry]
Name=Map Widget
+Name[ca]=Estris de mapes
Name[fi]=Karttaelementti
Name[nl]=Kaartwidget
+Name[pl]=Element interfejsu mapy
Name[pt]=Elemento de Mapa
Name[pt_BR]=Elemento de mapa
Name[sv]=Kartkomponenter
Name[uk]=Віджет карти
Name[x-test]=xxMap Widgetxx
Comment=Kexi plugin providing map browser form widget
+Comment[ca]=Connecto del Kexi que proporciona estris de formularis de navegació de mapes
Comment[nl]=Kexi-plug-in levert formulierwidget voor kaartenbrowser
+Comment[pl]=Wtyczka Kexi dostarczająca formularze przeglądania map
Comment[pt]='Plugin' do Kexi que oferece um elemento gráfico de formulários de navegação em mapas
Comment[pt_BR]=Plugin do Kexi que oferece um elemento gráfico de formulários de navegação em mapas
Comment[sv]=Kexi-insticksprogram som tillhandahåller formulärkomponent med kartbläddrare
Comment[uk]=Додаток до Kexi, що забезпечує роботу форми навігатора картою
Comment[x-test]=xxKexi plugin providing map browser form widgetxx
Type=Service
Icon=form
Encoding=UTF-8
X-KDE-Library=kexiforms_mapwidgetplugin
X-KDE-ServiceTypes=Kexi/FormWidgets
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.form.widgets.map
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-FormWidgetsFactoryGroup=
diff --git a/kexi/plugins/forms/widgets/webbrowser/kexiforms_webbrowserwidgetplugin.desktop b/kexi/plugins/forms/widgets/webbrowser/kexiforms_webbrowserwidgetplugin.desktop
index 77ce3d3e943..7b13a3d726e 100644
--- a/kexi/plugins/forms/widgets/webbrowser/kexiforms_webbrowserwidgetplugin.desktop
+++ b/kexi/plugins/forms/widgets/webbrowser/kexiforms_webbrowserwidgetplugin.desktop
@@ -1,33 +1,37 @@
[Desktop Entry]
Name=Web Browser Widget
+Name[ca]=Estri navegador web
Name[fi]=Verkkoselainelementti
Name[nl]=Webbrowserwidget
+Name[pl]=Element interfejsu przeglądarki sieciowej
Name[pt]=Elemento de Navegador Web
Name[pt_BR]=Elemento de navegador Web
Name[sv]=Webbläsarkomponent
Name[uk]=Віджет перегляду інтернету
Name[x-test]=xxWeb Browser Widgetxx
Comment=Kexi plugin providing web browser form widget
+Comment[ca]=Connecto del Kexi que proporciona estris de formularis de navegació web
Comment[nl]=Kexi-plug-in levert formulierwidget voor webbrowser
+Comment[pl]=Wtyczka Kexi dostarczająca formularze przeglądania sieci
Comment[pt]='Plugin' do Kexi que oferece um elemento gráfico de formulários de navegação Web
Comment[pt_BR]=Plugin do Kexi que oferece um elemento gráfico de formulários de navegação na Web
Comment[sv]=Kexi-insticksprogram som tillhandahåller formulärkomponent med webbläsare
Comment[uk]=Додаток до Kexi, що забезпечує роботу форми віджета навігатора інтернетом
Comment[x-test]=xxKexi plugin providing web browser form widgetxx
Type=Service
Icon=form
Encoding=UTF-8
X-KDE-Library=kexiforms_webbrowserwidgetplugin
X-KDE-ServiceTypes=Kexi/FormWidgets
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.form.widgets.web
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-FormWidgetsFactoryGroup=
diff --git a/kexi/plugins/importexport/csv/kexi_csvimportexportplugin.desktop b/kexi/plugins/importexport/csv/kexi_csvimportexportplugin.desktop
index 39ee7b66cff..9933ba88465 100644
--- a/kexi/plugins/importexport/csv/kexi_csvimportexportplugin.desktop
+++ b/kexi/plugins/importexport/csv/kexi_csvimportexportplugin.desktop
@@ -1,40 +1,44 @@
[Desktop Entry]
Name=CSV import/export
+Name[ca]=Importació/exportació de CSV
Name[de]=CSV-Import/Export
Name[fi]=CSV-tuonti/vienti
Name[nl]=CSV importeren/exporteren
+Name[pl]=Importowanie/eksportowanie CSV
Name[pt]=Importação/exportação de CSV
Name[pt_BR]=Importação/exportação de CSV
Name[sv]=CSV-import/export
Name[uk]=Імпортування і експортування CSV
Name[x-test]=xxCSV import/exportxx
Comment=Kexi Plugin for CSV data import/export
+Comment[ca]=Connector del Kexi d'importació/exportació de CSV
Comment[de]=Kexi-Modul für CSV-Daten-Import/Export
Comment[nl]=Kexi-plug-in voor importeren/exporteren van CSV-gegevens
+Comment[pl]=Wtyczka importu/eksportu danych CSV dla Kexi
Comment[pt]='Plugin' do Kexi para a importação/exportação de dados em CSV
Comment[pt_BR]=Plugin do Kexi para importação/exportação de dados CSV
Comment[sv]=Kexi insticksprogram för import/export av CSV-data
Comment[uk]=Додаток до Kexi для імпортування та експортування даних у форматі CSV
Comment[x-test]=xxKexi Plugin for CSV data import/exportxx
Type=Service
Icon=document-import
Encoding=UTF-8
X-KDE-Library=kexi_csvimportexportplugin
X-KDE-ServiceTypes=Kexi/ModalDialog
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.importexport.csv
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-GroupName=
X-Kexi-TypeName=
X-Kexi-ServiceTypesInUserMode=
X-Kexi-VisibleInProjectNavigator=false
X-Kexi-SupportsDataExport=false
X-Kexi-SupportsPrinting=false
diff --git a/kexi/plugins/importexport/csv/kexicsvwidgets.cpp b/kexi/plugins/importexport/csv/kexicsvwidgets.cpp
index b1f82ac500a..469f0aeebc2 100644
--- a/kexi/plugins/importexport/csv/kexicsvwidgets.cpp
+++ b/kexi/plugins/importexport/csv/kexicsvwidgets.cpp
@@ -1,392 +1,392 @@
/* This file is part of the KDE project
Copyright (C) 2005-2013 Jarosław Staniek <staniek@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kexicsvwidgets.h"
#include <QDir>
#include <QLabel>
#include <QVBoxLayout>
#include <QFrame>
#include <QVector>
#include <QLineEdit>
#include <KIconLoader>
#include <KIO/PixmapLoader>
#include <KLocalizedString>
#include <kexi_global.h>
#include <kexiutils/utils.h>
#define KEXICSV_OTHER_DELIMITER_INDEX 4
#define KEXICSV_OTHER_NUMBER_OF_SIGNS 2
#define KEXICSV_OTHER_commentSymbol_INDEX 1
#ifdef Q_CC_MSVC
Q_TEMPLATE_EXTERN template class Q_CORE_EXPORT QVector<QString>;
#endif
class KexiCSVInfoLabel::Private
{
public:
Private() {}
QLabel *leftLabel, *iconLbl, *fnameLbl, *commentLbl;
QFrame* separator;
};
class KexiCSVDelimiterWidget::Private
{
public:
Private() : availableDelimiters(KEXICSV_OTHER_DELIMITER_INDEX) {
availableDelimiters[0] = KEXICSV_DEFAULT_FILE_DELIMITER;
availableDelimiters[1] = ";";
availableDelimiters[2] = "\t";
availableDelimiters[3] = " ";
}
QString delimiter;
QVector<QString> availableDelimiters;
KComboBox* combo;
QLineEdit* delimiterEdit;
};
class KexiCSVCommentWidget::Private
{
public:
Private() : availablecommentSymbols(KEXICSV_OTHER_NUMBER_OF_SIGNS) {
availablecommentSymbols[0] = KEXICSV_DEFAULT_COMMENT_START;
availablecommentSymbols[1] = KEXICSV_DEFAULT_HASHSIGN;
}
QString commentSymbol;
QVector<QString> availablecommentSymbols;
KComboBox* combo;
QLineEdit* commentSymbolEdit;
};
KexiCSVDelimiterWidget::KexiCSVDelimiterWidget(bool lineEditOnBottom, QWidget * parent)
: QWidget(parent)
, d(new Private())
{
QBoxLayout *lyr = new QBoxLayout(lineEditOnBottom ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
setLayout(lyr);
KexiUtils::setMargins(lyr, 0);
lyr->setSpacing(KexiUtils::spacingHint());
d->combo = new KComboBox(this);
d->combo->setObjectName("KexiCSVDelimiterComboBox");
d->combo->addItem(xi18n("Comma \",\"")); //<-- KEXICSV_DEFAULT_FILE_DELIMITER
d->combo->addItem(xi18n("Semicolon \";\""));
d->combo->addItem(xi18n("Tabulator"));
d->combo->addItem(xi18n("Space \" \""));
d->combo->addItem(xi18n("Other"));
lyr->addWidget(d->combo);
setFocusProxy(d->combo);
d->delimiterEdit = new QLineEdit(this);
d->delimiterEdit->setObjectName("d->delimiterEdit");
d->delimiterEdit->setMaximumSize(QSize(30, 32767));
d->delimiterEdit->setMaxLength(1);
d->delimiterEdit->setVisible(false);
lyr->addWidget(d->delimiterEdit);
if (!lineEditOnBottom)
lyr->addStretch(2);
slotDelimiterChangedInternal(KEXICSV_DEFAULT_FILE_DELIMITER_INDEX); //this will init d->delimiter
connect(d->combo, SIGNAL(activated(int)),
this, SLOT(slotDelimiterChanged(int)));
connect(d->delimiterEdit, SIGNAL(returnPressed()),
this, SLOT(slotDelimiterLineEditReturnPressed()));
connect(d->delimiterEdit, SIGNAL(textChanged(QString)),
this, SLOT(slotDelimiterLineEditTextChanged(QString)));
slotDelimiterChangedInternal(KEXICSV_DEFAULT_FILE_DELIMITER_INDEX); //this will init d->delimiter
connect(d->combo, SIGNAL(activated(int)),
this, SLOT(slotDelimiterChanged(int)));
}
KexiCSVDelimiterWidget::~KexiCSVDelimiterWidget()
{
delete d;
}
void KexiCSVDelimiterWidget::slotDelimiterChanged(int index)
{
slotDelimiterChangedInternal(index);
if (index == KEXICSV_OTHER_DELIMITER_INDEX)
d->delimiterEdit->setFocus();
}
void KexiCSVDelimiterWidget::slotDelimiterChangedInternal(int index)
{
bool changed = false;
if (index > KEXICSV_OTHER_DELIMITER_INDEX)
return;
else if (index == KEXICSV_OTHER_DELIMITER_INDEX) {
changed = d->delimiter != d->delimiterEdit->text();
d->delimiter = d->delimiterEdit->text();
} else {
changed = d->delimiter != d->availableDelimiters[index];
d->delimiter = d->availableDelimiters[index];
}
d->delimiterEdit->setEnabled(index == KEXICSV_OTHER_DELIMITER_INDEX);
if (changed)
emit delimiterChanged(d->delimiter);
}
void KexiCSVDelimiterWidget::slotDelimiterLineEditReturnPressed()
{
if (d->combo->currentIndex() != KEXICSV_OTHER_DELIMITER_INDEX)
return;
slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
}
void KexiCSVDelimiterWidget::slotDelimiterLineEditTextChanged(const QString &)
{
slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
}
QString KexiCSVDelimiterWidget::delimiter() const
{
return d->delimiter;
}
void KexiCSVDelimiterWidget::setDelimiter(const QString& delimiter)
{
QVector<QString>::ConstIterator it = d->availableDelimiters.constBegin();
int index = 0;
for (; it != d->availableDelimiters.constEnd(); ++it, index++) {
if (*it == delimiter) {
d->combo->setCurrentIndex(index);
slotDelimiterChangedInternal(index);
return;
}
}
//else: set other (custom) delimiter
d->delimiterEdit->setText(delimiter);
d->combo->setCurrentIndex(KEXICSV_OTHER_DELIMITER_INDEX);
slotDelimiterChangedInternal(KEXICSV_OTHER_DELIMITER_INDEX);
}
//----------------------------------------------------
KexiCSVCommentWidget::KexiCSVCommentWidget(bool lineEditOnBottom, QWidget *parent) : QWidget(parent)
, d(new Private())
{
QBoxLayout *lyr = new QBoxLayout(lineEditOnBottom ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
setLayout(lyr);
KexiUtils::setMargins(lyr, 0);
lyr->setSpacing(KexiUtils::spacingHint());
d->combo = new KComboBox(this);
d->combo->setObjectName("KexiCSVcommentSymbolComboBox");
d->combo->addItem(xi18n("None"));
- d->combo->addItem(xi18n("Hashtag \"#\""));
+ d->combo->addItem(xi18n("Hash \"#\""));
lyr->addWidget(d->combo);
setFocusProxy(d->combo);
const int numberOfNonePosition = 0;
slotcommentSymbolChangedInternal(numberOfNonePosition);
connect(d->combo, SIGNAL(activated(int)),
this, SLOT(slotcommentSymbolChanged(int)));
}
KexiCSVCommentWidget::~KexiCSVCommentWidget()
{
delete d;
}
void KexiCSVCommentWidget::slotcommentSymbolChanged(int index)
{
slotcommentSymbolChangedInternal(index);
}
void KexiCSVCommentWidget::slotcommentSymbolChangedInternal(int index)
{
bool changed = false;
changed = d->commentSymbol != d->availablecommentSymbols[index];
d->commentSymbol = d->availablecommentSymbols[index];
if (changed)
emit commentSymbolChanged(d->commentSymbol);
}
QString KexiCSVCommentWidget::commentSymbol() const {
return d->commentSymbol;
}
void KexiCSVCommentWidget::setcommentSymbol(const QString& commentSymbol)
{
QVector<QString>::ConstIterator it = d->availablecommentSymbols.constBegin();
int index = 0;
for (; it != d->availablecommentSymbols.constEnd(); ++it, index++) {
if (*it == commentSymbol) {
d->combo->setCurrentIndex(index);
slotcommentSymbolChangedInternal(index);
return;
}
}
}
KexiCSVTextQuoteComboBox::KexiCSVTextQuoteComboBox(QWidget * parent)
: KComboBox(parent)
{
addItem("\"");
addItem("'");
addItem(xi18n("None"));
}
QString KexiCSVTextQuoteComboBox::textQuote() const
{
if (currentIndex() == 2)
return QString();
return currentText();
}
void KexiCSVTextQuoteComboBox::setTextQuote(const QString& textQuote)
{
QString q(textQuote.isEmpty() ? xi18n("None") : textQuote);
setCurrentIndex(findText(q));
}
//----------------------------------------------------
KexiCSVInfoLabel::KexiCSVInfoLabel(const QString& labelText, QWidget* parent, bool showFnameLine)
: QWidget(parent)
, d(new Private)
{
QVBoxLayout *vbox = new QVBoxLayout;
setLayout(vbox);
KexiUtils::setMargins(vbox, 0);
vbox->setSpacing(KexiUtils::spacingHint());
QGridLayout *topbox = new QGridLayout;
vbox->addLayout(topbox);
d->iconLbl = new QLabel(this);
d->iconLbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
d->iconLbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
topbox->addWidget(d->iconLbl, 0, 0, 2, 1);
topbox->addItem(new QSpacerItem(
KexiUtils::spacingHint(), KexiUtils::spacingHint(), QSizePolicy::Fixed, QSizePolicy::Fixed),
0, 1, 2, 1
);
d->leftLabel = new QLabel(labelText, this);
d->leftLabel->setMinimumWidth(130);
d->leftLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
QSizePolicy fnameLblSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
fnameLblSizePolicy.setHorizontalStretch(1);
d->leftLabel->setSizePolicy(fnameLblSizePolicy);
d->leftLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
topbox->addWidget(d->leftLabel, 0, 2, showFnameLine ? 1 : 2, 1);
if (showFnameLine) {
d->fnameLbl = new QLabel(this);
d->fnameLbl->setOpenExternalLinks(true);
d->fnameLbl->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
d->fnameLbl->setFocusPolicy(Qt::NoFocus);
d->fnameLbl->setTextFormat(Qt::PlainText);
QSizePolicy fnameLblSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
fnameLblSizePolicy.setHorizontalStretch(1);
d->fnameLbl->setSizePolicy(fnameLblSizePolicy);
d->fnameLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft);
d->fnameLbl->setWordWrap(true);
topbox->addWidget(d->fnameLbl, 1, 2); //Qt::AlignVCenter | Qt::AlignLeft);
}
else {
d->fnameLbl = 0;
}
d->commentLbl = new QLabel(this);
d->commentLbl->setOpenExternalLinks(true);
d->commentLbl->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
d->commentLbl->setFocusPolicy(Qt::NoFocus);
d->commentLbl->setTextFormat(Qt::PlainText);
d->commentLbl->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
d->commentLbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
d->commentLbl->setWordWrap(true);
topbox->addWidget(d->commentLbl, 0, 3, 2, 1);
d->separator = new QFrame(this);
d->separator->setFrameShape(QFrame::HLine);
d->separator->setFrameShadow(QFrame::Sunken);
vbox->addWidget(d->separator);
}
KexiCSVInfoLabel::~KexiCSVInfoLabel()
{
delete d;
}
void KexiCSVInfoLabel::setFileName(const QString& fileName)
{
if (!d->fnameLbl)
return;
d->fnameLbl->setText(QDir::toNativeSeparators(fileName));
if (!fileName.isEmpty()) {
d->iconLbl->setPixmap(
KIO::pixmapForUrl(QUrl(fileName), 0, KIconLoader::Desktop));
}
}
void KexiCSVInfoLabel::setLabelText(const QString& text)
{
d->leftLabel->setText(text);
}
void KexiCSVInfoLabel::setIcon(const QString& iconName)
{
d->iconLbl->setPixmap(DesktopIcon(iconName));
}
QLabel* KexiCSVInfoLabel::leftLabel() const
{
return d->leftLabel;
}
QLabel* KexiCSVInfoLabel::fileNameLabel() const
{
return d->fnameLbl;
}
QLabel* KexiCSVInfoLabel::commentLabel() const
{
return d->commentLbl;
}
QFrame* KexiCSVInfoLabel::separator() const {
return d->separator;
}
void KexiCSVInfoLabel::setCommentText(const QString& text)
{
d->commentLbl->setText(text);
}
//----------------------------------------------------
QStringList csvMimeTypes()
{
QStringList mimetypes;
mimetypes << "text/csv"
<< "text/tab-separated-value"
<< "text/plain"; // use application/octet-stream if you want
// all files, but then the others are not necessary
return mimetypes;
}
diff --git a/kexi/plugins/queries/kexi_queryplugin.desktop b/kexi/plugins/queries/kexi_queryplugin.desktop
index 64d2bc6498d..93892671d0d 100644
--- a/kexi/plugins/queries/kexi_queryplugin.desktop
+++ b/kexi/plugins/queries/kexi_queryplugin.desktop
@@ -1,69 +1,71 @@
[Desktop Entry]
Name=Query
Name[bg]=Заявка
Name[bs]=Upitnik
Name[ca]=Consulta
Name[ca@valencia]=Consulta
Name[cs]=Dotaz
Name[da]=Forespørgsel
Name[de]=Abfrage
Name[el]=Ερώτηση
Name[en_GB]=Query
Name[es]=Consulta
Name[et]=Päring
Name[eu]=Kontsulta
Name[fi]=Kysely
Name[fr]=Requête
Name[gl]=Consulta
Name[hu]=Lekérdezés
Name[ia]=Requesta
Name[it]=Interrogazione
Name[ja]=クエリ
Name[kk]=Сұраныс
Name[ko]=쿼리
Name[lt]=Užklausa
Name[nb]=Spørring
Name[nds]=Affraag
Name[nl]=Query
Name[pl]=Zapytanie
Name[pt]=Procura
Name[pt_BR]=Consulta
Name[ru]=Запрос
Name[sk]=Otázka
Name[sl]=Poizvedba
Name[sv]=Förfrågning
Name[ug]=سۈرۈشتۈر
Name[uk]=Запит
Name[x-test]=xxQueryxx
Name[zh_CN]=查询
Name[zh_TW]=查詢
Comment=Kexi plugin for handling queries
+Comment[ca]=Connector del Kexi per gestionar consultes
Comment[de]=Kexi-Modul für die Bearbeitung von Abfragen
Comment[nl]=Kexi-plug-in voor behandeling van queries
+Comment[pl]=Wtyczka Kexi do obsługi zapytań
Comment[pt]='Plugin' do Kexi para lidar com pesquisas
Comment[pt_BR]=Plugin do Kexi para lidar com consultas
Comment[sv]=Kexi-insticksprogram för att hantera förfrågningar
Comment[uk]=Додаток до Kexi для обробки запитів
Comment[x-test]=xxKexi plugin for handling queriesxx
Type=Service
Icon=query
Encoding=UTF-8
X-KDE-Library=kexi_queryplugin
X-KDE-ServiceTypes=Kexi/Viewer,Kexi/Designer,Kexi/Editor
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.query
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-GroupName=Queries
X-Kexi-TypeName=query
X-Kexi-ServiceTypesInUserMode=Kexi/Viewer
X-Kexi-VisibleInProjectNavigator=true
X-Kexi-SupportsDataExport=true
X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/reports/kexi_reportplugin.desktop b/kexi/plugins/reports/kexi_reportplugin.desktop
index d2506f013ac..da1b87b6687 100644
--- a/kexi/plugins/reports/kexi_reportplugin.desktop
+++ b/kexi/plugins/reports/kexi_reportplugin.desktop
@@ -1,67 +1,69 @@
[Desktop Entry]
Name=Report
Name[bg]=Доклад
Name[bs]=Izvještaj
Name[ca]=Informe
Name[ca@valencia]=Informe
Name[cs]=Hlášení
Name[da]=Rapport
Name[de]=Bericht
Name[el]=Αναφορά
Name[en_GB]=Report
Name[es]=Informe
Name[et]=Aruanne
Name[eu]=Txostena
Name[fi]=Raportti
Name[fr]=Rapport
Name[gl]=Informe
Name[hu]=Jelentés
Name[it]=Rapporto
Name[ja]=レポート
Name[kk]=Баянат
Name[ko]=보고서
Name[lt]=Ataskaita
Name[nb]=Rapport
Name[nl]=Rapport
Name[pl]=Raport
Name[pt]=Relatório
Name[pt_BR]=Relatório
Name[ru]=Отчёт
Name[sk]=Výkaz
Name[sl]=Poročilo
Name[sv]=Rapport
Name[ug]=دوكلات
Name[uk]=Звіт
Name[x-test]=xxReportxx
Name[zh_CN]=报表
Name[zh_TW]=報告
Comment=Kexi plugin for handling reports
+Comment[ca]=Connector del Kexi per gestionar informes
Comment[de]=Kexi-Modul für die Bearbeitung von Berichten
Comment[nl]=Kexi-plug-in voor behandeling van rapporten
+Comment[pl]=Wtyczka Kexi do obsługi raportów
Comment[pt]='Plugin' do Kexi para lidar com relatórios
Comment[pt_BR]=Plugin do Kexi para trabalhar com relatórios
Comment[sv]=Kexi-insticksprogram för att hantera rapporter
Comment[uk]=Додаток до Kexi для обробки звітів
Comment[x-test]=xxKexi plugin for handling reportsxx
Type=Service
Icon=report
Encoding=UTF-8
X-KDE-Library=kexi_reportplugin
X-KDE-ServiceTypes=Kexi/Viewer,Kexi/Designer
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.report
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-GroupName=Reports
X-Kexi-TypeName=report
X-Kexi-ServiceTypesInUserMode=Kexi/Viewer
X-Kexi-VisibleInProjectNavigator=true
X-Kexi-SupportsDataExport=false
X-Kexi-SupportsPrinting=false
diff --git a/kexi/plugins/tables/kexi_tableplugin.desktop b/kexi/plugins/tables/kexi_tableplugin.desktop
index 44740bfe244..cfd40773df5 100644
--- a/kexi/plugins/tables/kexi_tableplugin.desktop
+++ b/kexi/plugins/tables/kexi_tableplugin.desktop
@@ -1,69 +1,71 @@
[Desktop Entry]
Name=Table
Name[bg]=Таблица
Name[bs]=Tabela
Name[ca]=Taula
Name[ca@valencia]=Taula
Name[cs]=Tabulka
Name[da]=Tabel
Name[de]=Tabelle
Name[el]=Πίνακας
Name[en_GB]=Table
Name[es]=Tabla
Name[et]=Tabel
Name[eu]=Taula
Name[fi]=Taulu
Name[fr]=Tableau
Name[gl]=Táboa
Name[hu]=Táblázat
Name[ia]=Tabula
Name[it]=Tabella
Name[ja]=テーブル
Name[kk]=Кесте
Name[ko]=표
Name[lt]=Lentelė
Name[mr]=कोष्टक
Name[nb]=Tabell
Name[nl]=Tabel
Name[pl]=Tabela
Name[pt]=Tabela
Name[pt_BR]=Tabela
Name[ru]=Таблица
Name[sk]=Tabuľka
Name[sl]=Tabela
Name[sv]=Tabell
Name[ug]=جەدۋەل
Name[uk]=Таблиця
Name[x-test]=xxTablexx
Name[zh_CN]=表
Name[zh_TW]=Table
Comment=Kexi plugin for handling tables
+Comment[ca]=Connector del Kexi per gestionar taules
Comment[de]=Kexi-Modul für die Bearbeitung von Tabellen
Comment[nl]=Kexi-plug-in voor behandeling van tabellen
+Comment[pl]=Wtyczka Kexi do obsługi tabel
Comment[pt]='Plugin' do Kexi para lidar com tabelas
Comment[pt_BR]=Plugin do Kexi para trabalhar com tabelas
Comment[sv]=Kexi-insticksprogram för att hantera tabeller
Comment[uk]=Додаток до Kexi для обробки таблиць
Comment[x-test]=xxKexi plugin for handling tablesxx
Type=Service
Icon=table
Encoding=UTF-8
X-KDE-Library=kexi_tableplugin
X-KDE-ServiceTypes=Kexi/Viewer,Kexi/Designer
X-KDE-PluginInfo-Author=Kexi Team
X-KDE-PluginInfo-Email=kexi@kde.org
X-KDE-PluginInfo-Name=org.kexi-project.table
X-KDE-PluginInfo-Version=3.0
X-KDE-PluginInfo-Website=http://kexi-project.org
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=LGPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Kexi-GroupName=Tables
X-Kexi-TypeName=table
X-Kexi-ServiceTypesInUserMode=Kexi/Viewer
X-Kexi-VisibleInProjectNavigator=true
X-Kexi-SupportsDataExport=true
X-Kexi-SupportsPrinting=true
diff --git a/krita/benchmarks/kis_composition_benchmark.cpp b/krita/benchmarks/kis_composition_benchmark.cpp
index d227aa8d92c..4cedb73b0e6 100644
--- a/krita/benchmarks/kis_composition_benchmark.cpp
+++ b/krita/benchmarks/kis_composition_benchmark.cpp
@@ -1,861 +1,863 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2015 Thorsten Zachmann <zachmann@kde.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_composition_benchmark.h"
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoCompositeOpAlphaDarken.h>
#include <KoCompositeOpOver.h>
#include "KoOptimizedCompositeOpFactory.h"
// for calculation of the needed alignment
#include <config-vc.h>
#ifdef HAVE_VC
#include <Vc/Vc>
#include <Vc/IO>
#include <KoOptimizedCompositeOpOver32.h>
#include <KoOptimizedCompositeOpOver128.h>
#include <KoOptimizedCompositeOpAlphaDarken32.h>
#endif
// for posix_memalign()
#include <stdlib.h>
+#include <kis_debug.h>
+
const int alpha_pos = 3;
enum AlphaRange {
ALPHA_ZERO,
ALPHA_UNIT,
ALPHA_RANDOM
};
template <typename channel_type, class RandomGenerator>
inline channel_type generateAlphaValue(AlphaRange range, RandomGenerator &rnd) {
channel_type value = 0;
switch (range) {
case ALPHA_ZERO:
break;
case ALPHA_UNIT:
value = rnd.unit();
break;
case ALPHA_RANDOM:
value = rnd();
break;
}
return value;
}
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_smallint.hpp>
#include <boost/random/uniform_real.hpp>
template <typename channel_type>
struct RandomGenerator {
channel_type operator() () {
qFatal("Wrong template instantiation");
return channel_type(0);
}
channel_type unit() {
qFatal("Wrong template instantiation");
return channel_type(0);
}
};
template <>
struct RandomGenerator<quint8>
{
RandomGenerator(int seed)
: m_smallint(0,255),
m_rnd(seed)
{
}
quint8 operator() () {
return m_smallint(m_rnd);
}
quint8 unit() {
return KoColorSpaceMathsTraits<quint8>::unitValue;
}
boost::uniform_smallint<int> m_smallint;
boost::mt11213b m_rnd;
};
template <>
struct RandomGenerator<float>
{
RandomGenerator(int seed)
: m_rnd(seed)
{
}
float operator() () {
//return float(m_rnd()) / float(m_rnd.max());
return m_smallfloat(m_rnd);
}
float unit() {
return KoColorSpaceMathsTraits<float>::unitValue;
}
boost::uniform_real<float> m_smallfloat;
boost::mt11213b m_rnd;
};
template <>
struct RandomGenerator<double> : RandomGenerator<float>
{
RandomGenerator(int seed)
: RandomGenerator<float>(seed)
{
}
};
template <typename channel_type>
void generateDataLine(uint seed, int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange)
{
Q_ASSERT(numPixels >= 4);
RandomGenerator<channel_type> rnd(seed);
RandomGenerator<quint8> maskRnd(seed + 1);
channel_type *srcArray = reinterpret_cast<channel_type*>(srcPixels);
channel_type *dstArray = reinterpret_cast<channel_type*>(dstPixels);
for (int i = 0; i < numPixels; i++) {
for (int j = 0; j < 3; j++) {
channel_type s = rnd();
channel_type d = rnd();
*(srcArray++) = s;
*(dstArray++) = d;
}
channel_type sa = generateAlphaValue<channel_type>(srcAlphaRange, rnd);
channel_type da = generateAlphaValue<channel_type>(dstAlphaRange, rnd);
*(srcArray++) = sa;
*(dstArray++) = da;
*(mask++) = maskRnd();
}
}
void printData(int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask)
{
for (int i = 0; i < numPixels; i++) {
- qDebug() << "Src: "
+ dbgKrita << "Src: "
<< srcPixels[i*4] << "\t"
<< srcPixels[i*4+1] << "\t"
<< srcPixels[i*4+2] << "\t"
<< srcPixels[i*4+3] << "\t"
<< "Msk:" << mask[i];
- qDebug() << "Dst: "
+ dbgKrita << "Dst: "
<< dstPixels[i*4] << "\t"
<< dstPixels[i*4+1] << "\t"
<< dstPixels[i*4+2] << "\t"
<< dstPixels[i*4+3];
}
}
const int rowStride = 64;
const int totalRows = 64;
const QRect processRect(0,0,64,64);
const int numPixels = rowStride * totalRows;
const int numTiles = 1024;
struct Tile {
quint8 *src;
quint8 *dst;
quint8 *mask;
};
#include <stdint.h>
QVector<Tile> generateTiles(int size,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange,
const quint32 pixelSize)
{
QVector<Tile> tiles(size);
#ifdef HAVE_VC
const int vecSize = Vc::float_v::Size;
#else
const int vecSize = 1;
#endif
// the 256 are used to make sure that we have a good alignment no matter what build options are used.
- const size_t pixelAlignment = qMax(size_t(vecSize * sizeof(float)), 256ul);
- const size_t maskAlignment = qMax(size_t(vecSize), 256ul);
+ const size_t pixelAlignment = qMax(size_t(vecSize * sizeof(float)), size_t(256));
+ const size_t maskAlignment = qMax(size_t(vecSize), size_t(256));
for (int i = 0; i < size; i++) {
void *ptr = NULL;
int error = posix_memalign(&ptr, pixelAlignment, numPixels * pixelSize + srcAlignmentShift);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].src = (quint8*)ptr + srcAlignmentShift;
error = posix_memalign(&ptr, pixelAlignment, numPixels * pixelSize + dstAlignmentShift);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].dst = (quint8*)ptr + dstAlignmentShift;
error = posix_memalign(&ptr, maskAlignment, numPixels);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].mask = (quint8*)ptr;
if (pixelSize == 4) {
generateDataLine<quint8>(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange);
} else if (pixelSize == 16) {
generateDataLine<float>(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange);
} else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
}
return tiles;
}
void freeTiles(QVector<Tile> tiles,
const int srcAlignmentShift,
const int dstAlignmentShift)
{
foreach (const Tile &tile, tiles) {
free(tile.src - srcAlignmentShift);
free(tile.dst - dstAlignmentShift);
free(tile.mask);
}
}
template <typename channel_type>
inline bool fuzzyCompare(channel_type a, channel_type b, channel_type prec) {
return qAbs(a - b) <= prec;
}
template <typename channel_type>
inline bool comparePixels(channel_type *p1, channel_type *p2, channel_type prec) {
return (p1[3] == p2[3] && p1[3] == 0) ||
(fuzzyCompare(p1[0], p2[0], prec) &&
fuzzyCompare(p1[1], p2[1], prec) &&
fuzzyCompare(p1[2], p2[2], prec) &&
fuzzyCompare(p1[3], p2[3], prec));
}
template <typename channel_type>
bool compareTwoOpsPixels(QVector<Tile> &tiles, channel_type prec) {
channel_type *dst1 = reinterpret_cast<channel_type*>(tiles[0].dst);
channel_type *dst2 = reinterpret_cast<channel_type*>(tiles[1].dst);
channel_type *src1 = reinterpret_cast<channel_type*>(tiles[0].src);
channel_type *src2 = reinterpret_cast<channel_type*>(tiles[1].src);
for (int i = 0; i < numPixels; i++) {
if (!comparePixels<channel_type>(dst1, dst2, prec)) {
- qDebug() << "Wrong result:" << i;
- qDebug() << "Act: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
- qDebug() << "Exp: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
- qDebug() << "Dif: " << dst1[0] - dst2[0] << dst1[1] - dst2[1] << dst1[2] - dst2[2] << dst1[3] - dst2[3];
+ dbgKrita << "Wrong result:" << i;
+ dbgKrita << "Act: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
+ dbgKrita << "Exp: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
+ dbgKrita << "Dif: " << dst1[0] - dst2[0] << dst1[1] - dst2[1] << dst1[2] - dst2[2] << dst1[3] - dst2[3];
channel_type *s1 = src1 + 4 * i;
channel_type *s2 = src2 + 4 * i;
- qDebug() << "SrcA:" << s1[0] << s1[1] << s1[2] << s1[3];
- qDebug() << "SrcE:" << s2[0] << s2[1] << s2[2] << s2[3];
+ dbgKrita << "SrcA:" << s1[0] << s1[1] << s1[2] << s1[3];
+ dbgKrita << "SrcE:" << s2[0] << s2[1] << s2[2] << s2[3];
- qDebug() << "MskA:" << tiles[0].mask[i];
- qDebug() << "MskE:" << tiles[1].mask[i];
+ dbgKrita << "MskA:" << tiles[0].mask[i];
+ dbgKrita << "MskE:" << tiles[1].mask[i];
return false;
}
dst1 += 4;
dst2 += 4;
}
return true;
}
bool compareTwoOps(bool haveMask, const KoCompositeOp *op1, const KoCompositeOp *op2)
{
Q_ASSERT(op1->colorSpace()->pixelSize() == op2->colorSpace()->pixelSize());
const quint32 pixelSize = op1->colorSpace()->pixelSize();
const int alignment = 16;
QVector<Tile> tiles = generateTiles(2, alignment, alignment, ALPHA_RANDOM, ALPHA_RANDOM, op1->colorSpace()->pixelSize());
KoCompositeOp::ParameterInfo params;
params.dstRowStride = 4 * rowStride;
params.srcRowStride = 4 * rowStride;
params.maskRowStride = rowStride;
params.rows = processRect.height();
params.cols = processRect.width();
// This is a hack as in the old version we get a rounding of opacity to this value
params.opacity = float(Arithmetic::scale<quint8>(0.5*1.0f))/255.0;
params.flow = 0.3*1.0f;
params.channelFlags = QBitArray();
params.dstRowStart = tiles[0].dst;
params.srcRowStart = tiles[0].src;
params.maskRowStart = haveMask ? tiles[0].mask : 0;
op1->composite(params);
params.dstRowStart = tiles[1].dst;
params.srcRowStart = tiles[1].src;
params.maskRowStart = haveMask ? tiles[1].mask : 0;
op2->composite(params);
bool compareResult = true;
if (pixelSize == 4) {
compareResult = compareTwoOpsPixels<quint8>(tiles, 10);
}
else if (pixelSize == 16) {
compareResult = compareTwoOpsPixels<float>(tiles, 0);
}
else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
freeTiles(tiles, alignment, alignment);
return compareResult;
}
QString getTestName(bool haveMask,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange)
{
QString testName;
testName +=
!srcAlignmentShift && !dstAlignmentShift ? "Aligned " :
!srcAlignmentShift && dstAlignmentShift ? "SrcUnalig " :
srcAlignmentShift && !dstAlignmentShift ? "DstUnalig " :
srcAlignmentShift && dstAlignmentShift ? "Unaligned " : "###";
testName += haveMask ? "Mask " : "NoMask ";
testName +=
srcAlphaRange == ALPHA_RANDOM ? "SrcRand " :
srcAlphaRange == ALPHA_ZERO ? "SrcZero " :
srcAlphaRange == ALPHA_UNIT ? "SrcUnit " : "###";
testName +=
dstAlphaRange == ALPHA_RANDOM ? "DstRand" :
dstAlphaRange == ALPHA_ZERO ? "DstZero" :
dstAlphaRange == ALPHA_UNIT ? "DstUnit" : "###";
return testName;
}
void benchmarkCompositeOp(const KoCompositeOp *op,
bool haveMask,
qreal opacity,
qreal flow,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange)
{
QString testName = getTestName(haveMask, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange);
QVector<Tile> tiles =
generateTiles(numTiles, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange, op->colorSpace()->pixelSize());
const int tileOffset = 4 * (processRect.y() * rowStride + processRect.x());
KoCompositeOp::ParameterInfo params;
params.dstRowStride = 4 * rowStride;
params.srcRowStride = 4 * rowStride;
params.maskRowStride = rowStride;
params.rows = processRect.height();
params.cols = processRect.width();
params.opacity = opacity;
params.flow = flow;
params.channelFlags = QBitArray();
QTime timer;
timer.start();
foreach (const Tile &tile, tiles) {
params.dstRowStart = tile.dst + tileOffset;
params.srcRowStart = tile.src + tileOffset;
params.maskRowStart = haveMask ? tile.mask : 0;
op->composite(params);
}
- qDebug() << testName << "RESULT:" << timer.elapsed() << "msec";
+ dbgKrita << testName << "RESULT:" << timer.elapsed() << "msec";
freeTiles(tiles, srcAlignmentShift, dstAlignmentShift);
}
void benchmarkCompositeOp(const KoCompositeOp *op, const QString &postfix)
{
- qDebug() << "Testing Composite Op:" << op->id() << "(" << postfix << ")";
+ dbgKrita << "Testing Composite Op:" << op->id() << "(" << postfix << ")";
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 8, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 8, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 4, 8, ALPHA_RANDOM, ALPHA_RANDOM);
/// --- Vary the content of the source and destination
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_RANDOM);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_RANDOM);
/// ---
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_ZERO);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_ZERO);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_ZERO);
/// ---
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_UNIT);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_UNIT);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_UNIT);
}
#ifdef HAVE_VC
template<class Compositor>
void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1, quint32 pixelSize = 4)
{
QVector<Tile> tiles =
generateTiles(2, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM, pixelSize);
const int vecSize = Vc::float_v::Size;
const int numBlocks = numPixels / vecSize;
quint8 *src1 = tiles[0].src;
quint8 *dst1 = tiles[0].dst;
quint8 *msk1 = tiles[0].mask;
quint8 *src2 = tiles[1].src;
quint8 *dst2 = tiles[1].dst;
quint8 *msk2 = tiles[1].mask;
KoCompositeOp::ParameterInfo params;
params.opacity = opacity;
params.flow = flow;
if (averageOpacity >= 0.0) {
params._lastOpacityData = averageOpacity;
params.lastOpacity = &params._lastOpacityData;
}
params.channelFlags = QBitArray();
typename Compositor::OptionalParams optionalParams(params);
// The error count is needed as 38.5 gets rounded to 38 instead of 39 in the vc version.
int errorcount = 0;
for (int i = 0; i < numBlocks; i++) {
Compositor::template compositeVector<true,true, VC_IMPL>(src1, dst1, msk1, params.opacity, optionalParams);
for (int j = 0; j < vecSize; j++) {
//if (8 * i + j == 7080) {
- // qDebug() << "src: " << src2[0] << src2[1] << src2[2] << src2[3];
- // qDebug() << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
- // qDebug() << "msk:" << msk2[0];
+ // dbgKrita << "src: " << src2[0] << src2[1] << src2[2] << src2[3];
+ // dbgKrita << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
+ // dbgKrita << "msk:" << msk2[0];
//}
Compositor::template compositeOnePixelScalar<true, VC_IMPL>(src2, dst2, msk2, params.opacity, optionalParams);
bool compareResult = true;
if (pixelSize == 4) {
compareResult = comparePixels<quint8>(dst1, dst2, 0);
if (!compareResult) {
++errorcount;
compareResult = comparePixels<quint8>(dst1, dst2, 1);
if (!compareResult) {
++errorcount;
}
}
}
else if (pixelSize == 16) {
compareResult = comparePixels<float>(reinterpret_cast<float*>(dst1), reinterpret_cast<float*>(dst2), 0);
}
else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
if(!compareResult || errorcount > 1) {
- qDebug() << "Wrong rounding in pixel:" << 8 * i + j;
- qDebug() << "Vector version: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
- qDebug() << "Scalar version: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
+ dbgKrita << "Wrong rounding in pixel:" << 8 * i + j;
+ dbgKrita << "Vector version: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
+ dbgKrita << "Scalar version: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
- qDebug() << "src:" << src1[0] << src1[1] << src1[2] << src1[3];
- qDebug() << "msk:" << msk1[0];
+ dbgKrita << "src:" << src1[0] << src1[1] << src1[2] << src1[3];
+ dbgKrita << "msk:" << msk1[0];
QFAIL("Wrong rounding");
}
src1 += pixelSize;
dst1 += pixelSize;
src2 += pixelSize;
dst2 += pixelSize;
msk1++;
msk2++;
}
}
freeTiles(tiles, 0, 0);
}
#endif
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_03()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_05()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.5);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_07()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.7);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10_08()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0,0.8);
#endif
}
void KisCompositionBenchmark::checkRoundingOver()
{
#ifdef HAVE_VC
checkRounding<OverCompositor32<quint8, quint32, false, true> >(0.5, 0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingOverRgbaF32()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 0.3, -1, 16);
#endif
}
void KisCompositionBenchmark::compareAlphaDarkenOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareAlphaDarkenOpsNoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareOverOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareOverOpsNoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareRgbF32OverOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp128(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoRgbF32Traits>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, "Optimized");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeOverLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpOver<KoBgrU8Traits>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeOverOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp32(cs);
benchmarkCompositeOp(op, "Optimized");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeOverLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = new KoCompositeOpOver<KoRgbF32Traits>(cs);
benchmarkCompositeOp(op, "RGBF32 Legacy");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeOverOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp128(cs);
benchmarkCompositeOp(op, "RGBF32 Optimized");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenReal_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_ALPHA_DARKEN);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
}
void KisCompositionBenchmark::testRgb8CompositeOverReal_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_OVER);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
}
void KisCompositionBenchmark::testRgb8CompositeCopyLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_COPY);
benchmarkCompositeOp(op, "Copy");
}
void KisCompositionBenchmark::benchmarkMemcpy()
{
QVector<Tile> tiles =
generateTiles(numTiles, 0, 0, ALPHA_UNIT, ALPHA_UNIT, 4);
QBENCHMARK_ONCE {
foreach (const Tile &tile, tiles) {
memcpy(tile.dst, tile.src, 4 * numPixels);
}
}
freeTiles(tiles, 0, 0);
}
#ifdef HAVE_VC
const int vecSize = Vc::float_v::Size;
const size_t uint8VecAlignment = qMax(vecSize * sizeof(quint8), sizeof(void*));
const size_t uint32VecAlignment = qMax(vecSize * sizeof(quint32), sizeof(void*));
const size_t floatVecAlignment = qMax(vecSize * sizeof(float), sizeof(void*));
#endif
void KisCompositionBenchmark::benchmarkUintFloat()
{
#ifdef HAVE_VC
const int dataSize = 4096;
void *ptr = NULL;
int error = posix_memalign(&ptr, uint8VecAlignment, dataSize);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint8 *iData = (quint8*)ptr;
error = posix_memalign(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::Size) {
// convert uint -> float directly, this causes
// static_cast helper be called
Vc::float_v b(Vc::uint_v(iData + i));
b.store(fData + i);
}
}
free(iData);
free(fData);
#endif
}
void KisCompositionBenchmark::benchmarkUintIntFloat()
{
#ifdef HAVE_VC
const int dataSize = 4096;
void *ptr = NULL;
int error = posix_memalign(&ptr, uint8VecAlignment, dataSize);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint8 *iData = (quint8*)ptr;
error = posix_memalign(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::Size) {
// convert uint->int->float, that avoids special sign
// treating, and gives 2.6 times speedup
Vc::float_v b(Vc::int_v(Vc::uint_v(iData + i)));
b.store(fData + i);
}
}
free(iData);
free(fData);
#endif
}
void KisCompositionBenchmark::benchmarkFloatUint()
{
#ifdef HAVE_VC
const int dataSize = 4096;
void *ptr = NULL;
int error = posix_memalign(&ptr, uint32VecAlignment, dataSize * sizeof(quint32));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint32 *iData = (quint32*)ptr;
error = posix_memalign(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::Size) {
// conversion float -> uint
Vc::uint_v b(Vc::float_v(fData + i));
b.store(iData + i);
}
}
free(iData);
free(fData);
#endif
}
void KisCompositionBenchmark::benchmarkFloatIntUint()
{
#ifdef HAVE_VC
const int dataSize = 4096;
void *ptr = NULL;
int error = posix_memalign(&ptr, uint32VecAlignment, dataSize * sizeof(quint32));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint32 *iData = (quint32*)ptr;
error = posix_memalign(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::Size) {
// conversion float -> int -> uint
Vc::uint_v b(Vc::int_v(Vc::float_v(fData + i)));
b.store(iData + i);
}
}
free(iData);
free(fData);
#endif
}
QTEST_KDEMAIN(KisCompositionBenchmark, GUI)
diff --git a/krita/benchmarks/kis_filter_selections_benchmark.cpp b/krita/benchmarks/kis_filter_selections_benchmark.cpp
index be5cc082f81..300906c54f7 100644
--- a/krita/benchmarks/kis_filter_selections_benchmark.cpp
+++ b/krita/benchmarks/kis_filter_selections_benchmark.cpp
@@ -1,294 +1,294 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_filter_selections_benchmark.h"
#include "kis_painter.h"
#include <qtest_kde.h>
#include "testutil.h"
#include "kis_selection.h"
#include "kis_transaction.h"
#include <KoCompositeOpRegistry.h>
#include "kis_datamanager.h"
#define NUM_CYCLES 50
#define WARMUP_CYCLES 2
#define SHOW_WARMUPS 0
/**
* Our filters don't know anything about applyAlphaU8Mask
* That's why they treat semy-selected pixels badly
*
* If you have a hack in KisFilter - processSpecial(..) method
* that takes into account this alpha mask then activate
* the following define
*/
#define USE_GOOD_SELECTIONS 0
#define USE_UTIME 0
#if(USE_UTIME==1)
#include <sys/times.h>
class KisTimeCounter
{
public:
KisTimeCounter() {
m_factor = double(sysconf(_SC_CLK_TCK)) / 1000.0;
restart();
}
void restart() {
times(&m_startTime);
}
double elapsed() {
struct tms endTime;
times(&endTime);
return double(endTime.tms_utime - m_startTime.tms_utime) / m_factor;
}
private:
struct tms m_startTime;
double m_factor;
};
#else /* if(USE_UTIME==0) */
typedef QTime KisTimeCounter;
#endif
void KisFilterSelectionsBenchmark::initSelection()
{
m_selection = new KisSelection();
KisPixelSelectionSP pixelSelection = m_selection->pixelSelection();
//67.2% deselected
- qDebug() << "Deselected: 67.2%";
+ dbgKrita << "Deselected: 67.2%";
pixelSelection->dataManager()->clear(75, 75, 500, 320, 255);
pixelSelection->dataManager()->clear(100, 100, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(150, 150, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(200, 200, 50, 50, quint8(0));
pixelSelection->dataManager()->clear(375, 195, 200, 200, quint8(0));
pixelSelection->dataManager()->clear(75, 195, 200, 200, quint8(0));
pixelSelection->dataManager()->clear(375, 75, 150, 150, quint8(0));
pixelSelection->dataManager()->clear(205, 105, 50, 50, quint8(128));
// 94.9% deselected
-// qDebug() << "Deselected: 94.9%";
+// dbgKrita << "Deselected: 94.9%";
// pixelSelection->dataManager()->clear(75,75,500,320,255);
// pixelSelection->dataManager()->clear(80,80,490,310,quint8(0));
pixelSelection->convertToQImage(0).save("TEST_FILTER_SELECTION.png");
}
void KisFilterSelectionsBenchmark::initFilter(const QString &name)
{
m_filter = KisFilterRegistry::instance()->value(name);
Q_ASSERT(m_filter);
m_configuration = m_filter->defaultConfiguration(0);
- qDebug() << "Filter initialized:" << name;
+ dbgKrita << "Filter initialized:" << name;
}
void KisFilterSelectionsBenchmark::testFilter(const QString &name)
{
blockSignals(true);
initFilter(name);
testUsualSelections(WARMUP_CYCLES);
testUsualSelections(NUM_CYCLES);
testGoodSelections(WARMUP_CYCLES);
testGoodSelections(NUM_CYCLES);
testNoSelections(WARMUP_CYCLES);
testNoSelections(NUM_CYCLES);
testBitBltWOSelections(WARMUP_CYCLES);
testBitBltWOSelections(NUM_CYCLES);
testBitBltSelections(WARMUP_CYCLES);
testBitBltSelections(NUM_CYCLES);
blockSignals(false);
}
void KisFilterSelectionsBenchmark::testAll()
{
initSelection();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
m_device = new KisPaintDevice(cs);
m_device->convertFromQImage(image, 0, 0, 0);
testFilter("brightnesscontrast");
testFilter("invert");
// testFilter("levels");
}
void KisFilterSelectionsBenchmark::testUsualSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(projection, 0);
m_filter->process(m_device, projection, m_selection, filterRect, m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__USUAL_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "Selections inside filter:\t\t" << avTime;
+ dbgKrita << "Selections inside filter:\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testNoSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(projection, 0);
m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__NO_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "No Selections:\t\t\t\t" << avTime;
+ dbgKrita << "No Selections:\t\t\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testGoodSelections(int num)
{
#if(USE_GOOD_SELECTIONS==1)
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
KisConstProcessingInformation src(m_device, filterRect.topLeft(), m_selection);
KisProcessingInformation dst(projection, filterRect.topLeft(), 0);
timer.restart();
for (int i = 0; i < num; i++) {
KisTransaction transac(0, projection, 0);
m_filter->processSpecial(src, dst, filterRect.size(), m_configuration, 0);
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__GOOD_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "Selections with alpha (filter):\t" << avTime;
+ dbgKrita << "Selections with alpha (filter):\t" << avTime;
#else /* if (USE_GOOD_SELECTIONS!=1) */
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "Selections with alpha (filter):\t [Disabled]";
+ dbgKrita << "Selections with alpha (filter):\t [Disabled]";
#endif
}
void KisFilterSelectionsBenchmark::testBitBltWOSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace());
KisTransaction transac(cacheDevice, 0);
m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0);
KisPainter painter(projection);
painter.beginTransaction();
painter.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN));
painter.bitBlt(filterRect.topLeft(), cacheDevice, filterRect);
painter.deleteTransaction();
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__BITBLT_WO_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "bitBlt w/o sel:\t\t\t" << avTime;
+ dbgKrita << "bitBlt w/o sel:\t\t\t" << avTime;
}
void KisFilterSelectionsBenchmark::testBitBltSelections(int num)
{
KisPaintDeviceSP projection =
new KisPaintDevice(m_device->colorSpace());
double avTime;
KisTimeCounter timer;
QRect filterRect = m_selection->selectedExactRect();
timer.restart();
for (int i = 0; i < num; i++) {
KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace());
KisTransaction transac(cacheDevice, 0);
m_filter->process(m_device, cacheDevice, 0, filterRect, m_configuration, 0);
KisPainter gc(projection);
gc.beginTransaction();
gc.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN));
gc.setSelection(m_selection);
gc.bitBlt(filterRect.topLeft(), cacheDevice, filterRect);
gc.deleteTransaction();
}
avTime = double(timer.elapsed()) / num;
projection->convertToQImage(0).save("TFS__BITBLT_WITH_SELECTIONS.png");
if (num > WARMUP_CYCLES || SHOW_WARMUPS)
- qDebug() << "bitBlt with sel:\t\t\t" << avTime;
+ dbgKrita << "bitBlt with sel:\t\t\t" << avTime;
}
QTEST_KDEMAIN(KisFilterSelectionsBenchmark, GUI)
diff --git a/krita/benchmarks/kis_low_memory_benchmark.cpp b/krita/benchmarks/kis_low_memory_benchmark.cpp
index 6161b5dc5cb..b7d4b653514 100644
--- a/krita/benchmarks/kis_low_memory_benchmark.cpp
+++ b/krita/benchmarks/kis_low_memory_benchmark.cpp
@@ -1,231 +1,231 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_low_memory_benchmark.h"
#include <qtest_kde.h>
#include "kis_benchmark_values.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include "kis_paint_device.h"
#include "kis_painter.h"
#include <kis_paint_information.h>
#include <kis_paintop_registry.h>
#include <kis_paintop_preset.h>
#include "tiles3/kis_tile_data_store.h"
#include "kis_surrogate_undo_adapter.h"
#include "kis_image_config.h"
#define LOAD_PRESET_OR_RETURN(preset, fileName) \
- if(!preset->load()) { qDebug() << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \
- else qDebug() << "Loaded preset:" << fileName
+ if(!preset->load()) { dbgKrita << "Preset" << fileName << "was NOT loaded properly. Done."; return; } \
+ else dbgKrita << "Loaded preset:" << fileName
#define HUGE_IMAGE_SIZE 8000
/**
* This benchmark runs a series of huge strokes on a canvas with a
* particular configuration of the swapper/pooler and history
* management. After the test is done you can visualize the results
* with the GNU Octave. Please use kis_low_memory_show_report.m file
* for that.
*/
void KisLowMemoryBenchmark::benchmarkWideArea(const QString presetFileName,
const QRectF &rect, qreal vstep,
int numCycles,
bool createTransaction,
int hardLimitMiB,
int softLimitMiB,
int poolLimitMiB,
int index)
{
KisPaintOpPresetSP preset = new KisPaintOpPreset(QString(FILES_DATA_DIR) + QDir::separator() + presetFileName);
LOAD_PRESET_OR_RETURN(preset, presetFileName);
/**
* Initialize image and painter
*/
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, HUGE_IMAGE_SIZE, HUGE_IMAGE_SIZE, colorSpace, "stroke sample image", true);
KisLayerSP layer = new KisPaintLayer(image, "temporary for stroke sample", OPACITY_OPAQUE_U8, colorSpace);
KisLayerSP layerExtra = new KisPaintLayer(image, "temporary for threading", OPACITY_OPAQUE_U8, colorSpace);
image->addNode(layer, image->root());
image->addNode(layerExtra, image->root());
KisPainter *painter = new KisPainter(layer->paintDevice());
painter->setPaintColor(KoColor(Qt::black, colorSpace));
painter->setPaintOpPreset(preset, layer, image);
/**
* A simple adapter that will store all the transactions for us
*/
KisSurrogateUndoAdapter undoAdapter;
/**
* Reset configuration to the desired settings
*/
KisImageConfig config;
qreal oldHardLimit = config.memoryHardLimitPercent();
qreal oldSoftLimit = config.memorySoftLimitPercent();
qreal oldPoolLimit = config.memoryPoolLimitPercent();
const qreal _MiB = 100.0 / KisImageConfig::totalRAM();
config.setMemoryHardLimitPercent(hardLimitMiB * _MiB);
config.setMemorySoftLimitPercent(softLimitMiB * _MiB);
config.setMemoryPoolLimitPercent(poolLimitMiB * _MiB);
KisTileDataStore::instance()->testingRereadConfig();
/**
* Create an empty the log file
*/
QString fileName;
fileName = QString("log_%1_%2_%3_%4_%5.txt")
.arg(createTransaction)
.arg(hardLimitMiB)
.arg(softLimitMiB)
.arg(poolLimitMiB)
.arg(index);
QFile logFile(fileName);
logFile.open(QFile::WriteOnly | QFile::Truncate);
QTextStream logStream(&logFile);
logStream.setFieldWidth(10);
logStream.setFieldAlignment(QTextStream::AlignRight);
/**
* Start painting on the image
*/
QTime cycleTime;
QTime lineTime;
cycleTime.start();
lineTime.start();
qreal rectBottom = rect.y() + rect.height();
for (int i = 0; i < numCycles; i++) {
cycleTime.restart();
QLineF line(rect.topLeft(), rect.topLeft() + QPointF(rect.width(), 0));
if (createTransaction) {
painter->beginTransaction();
}
KisDistanceInformation currentDistance;
while(line.y1() < rectBottom) {
lineTime.restart();
KisPaintInformation pi1(line.p1(), 0.0);
KisPaintInformation pi2(line.p2(), 1.0);
painter->paintLine(pi1, pi2, &currentDistance);
painter->device()->setDirty(painter->takeDirtyRegion());
logStream << "L 1" << i << lineTime.elapsed()
<< KisTileDataStore::instance()->numTilesInMemory() * 16
<< KisTileDataStore::instance()->numTiles() * 16
<< createTransaction << endl;
line.translate(0, vstep);
}
painter->device()->setDirty(painter->takeDirtyRegion());
if (createTransaction) {
painter->endTransaction(&undoAdapter);
}
// comment/uncomment to emulate user waiting after the stroke
QTest::qSleep(1000);
logStream << "C 2" << i << cycleTime.elapsed()
<< KisTileDataStore::instance()->numTilesInMemory() * 16
<< KisTileDataStore::instance()->numTiles() * 16
<< createTransaction
<< config.memoryHardLimitPercent() / _MiB
<< config.memorySoftLimitPercent() / _MiB
<< config.memoryPoolLimitPercent() / _MiB << endl;
}
config.setMemoryHardLimitPercent(oldHardLimit * _MiB);
config.setMemorySoftLimitPercent(oldSoftLimit * _MiB);
config.setMemoryPoolLimitPercent(oldPoolLimit * _MiB);
delete painter;
}
void KisLowMemoryBenchmark::unlimitedMemoryNoHistoryNoPool()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, false,
3000, 3000, 0, 0);
}
void KisLowMemoryBenchmark::unlimitedMemoryHistoryNoPool()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
3000, 3000, 0, 0);
}
void KisLowMemoryBenchmark::unlimitedMemoryHistoryPool50()
{
QString presetFileName = "autobrush_300px.kpp";
// one cycle takes about 48 MiB of memory (total 960 MiB)
QRectF rect(150,150,4000,4000);
qreal step = 250;
int numCycles = 20;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
3000, 3000, 50, 0);
}
void KisLowMemoryBenchmark::memory2000History100Pool500HugeBrush()
{
QString presetFileName = "BIG_TESTING.kpp";
// one cycle takes about 316 MiB of memory (total 3+ GiB)
QRectF rect(150,150,7850,7850);
qreal step = 250;
int numCycles = 10;
benchmarkWideArea(presetFileName, rect, step, numCycles, true,
2000, 600, 500, 0);
}
QTEST_KDEMAIN(KisLowMemoryBenchmark, GUI)
diff --git a/krita/benchmarks/kis_painter_benchmark.cpp b/krita/benchmarks/kis_painter_benchmark.cpp
index f8db22e136f..ca9a187898d 100644
--- a/krita/benchmarks/kis_painter_benchmark.cpp
+++ b/krita/benchmarks/kis_painter_benchmark.cpp
@@ -1,278 +1,278 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if defined(_WIN32) || defined(_WIN64)
#include <stdlib.h>
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
#include <qtest_kde.h>
#include <QImage>
-#include <QDebug>
+#include <kis_debug.h>
#include "kis_painter_benchmark.h"
#include "kis_benchmark_values.h"
#include "kis_paint_device.h"
#include "kis_fixed_paint_device.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include <kis_painter.h>
#include <kis_types.h>
#define SAVE_OUTPUT
#define CYCLES 20
static const int LINE_COUNT = 100;
static const int LINE_WIDTH = 1;
void KisPainterBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_color = KoColor(m_colorSpace);
m_color.fromQColor(Qt::red);
srand48(0);
for (int i = 0; i < LINE_COUNT ;i++){
m_points.append( QPointF(drand48() * TEST_IMAGE_WIDTH, drand48() * TEST_IMAGE_HEIGHT) );
m_points.append( QPointF(drand48() * TEST_IMAGE_WIDTH, drand48() * TEST_IMAGE_HEIGHT) );
}
}
void KisPainterBenchmark::cleanupTestCase()
{
}
void KisPainterBenchmark::benchmarkBitBlt()
{
KisPaintDeviceSP src = new KisPaintDevice(m_colorSpace);
KisPaintDeviceSP dst = new KisPaintDevice(m_colorSpace);
src->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
dst->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
KisPainter gc(dst);
QPoint pos(0,0);
QRect rc(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT);
QBENCHMARK{
for (int i = 0; i < CYCLES ; i++){
gc.bitBlt(pos,src,rc);
}
}
}
void KisPainterBenchmark::benchmarkFastBitBlt()
{
KisPaintDeviceSP src = new KisPaintDevice(m_colorSpace);
KisPaintDeviceSP dst = new KisPaintDevice(m_colorSpace);
src->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
dst->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
KisPainter gc(dst);
gc.setCompositeOp(m_colorSpace->compositeOp(COMPOSITE_COPY));
QPoint pos(0,0);
QRect rc(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT);
QBENCHMARK{
for (int i = 0; i < CYCLES ; i++){
gc.bitBlt(pos,src,rc);
}
}
}
void KisPainterBenchmark::benchmarkBitBltSelection()
{
KisPaintDeviceSP src = new KisPaintDevice(m_colorSpace);
KisPaintDeviceSP dst = new KisPaintDevice(m_colorSpace);
src->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
dst->fill(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, m_color.data());
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(QRect(0, 0, TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT));
selection->updateProjection();
KisPainter gc(dst);
gc.setSelection(selection);
QPoint pos(0,0);
QRect rc(0,0,TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT);
QBENCHMARK{
for (int i = 0; i < CYCLES ; i++){
gc.bitBlt(pos,src,rc);
}
}
}
void KisPainterBenchmark::benchmarkFixedBitBlt()
{
QImage img(TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,QImage::Format_ARGB32);
img.fill(255);
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(m_colorSpace);
fdev->convertFromQImage(img, 0);
KisPaintDeviceSP dst = new KisPaintDevice(m_colorSpace);
KisPainter gc(dst);
QPoint pos(0, 0);
QRect rc = img.rect();
QBENCHMARK{
for (int i = 0; i < CYCLES ; i++){
gc.bltFixed(pos,fdev,rc);
}
}
}
void KisPainterBenchmark::benchmarkFixedBitBltSelection()
{
QImage img(TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,QImage::Format_ARGB32);
img.fill(128);
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(m_colorSpace);
fdev->convertFromQImage(img, 0);
KisPaintDeviceSP dst = new KisPaintDevice(m_colorSpace);
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(QRect(0, 0, TEST_IMAGE_WIDTH , TEST_IMAGE_HEIGHT));
selection->updateProjection();
KisPainter gc(dst);
gc.setSelection(selection);
QPoint pos(0, 0);
QRect rc = img.rect();
QBENCHMARK{
for (int i = 0; i < CYCLES ; i++){
gc.bltFixed(pos,fdev,rc);
}
}
}
void KisPainterBenchmark::benchmarkDrawThickLine()
{
KisPaintDeviceSP dev = new KisPaintDevice(m_colorSpace);
KoColor color(m_colorSpace);
color.fromQColor(Qt::white);
dev->clear();
dev->fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,color.data());
color.fromQColor(Qt::black);
KisPainter painter(dev);
painter.setPaintColor(color);
QBENCHMARK{
for (int i = 0; i < LINE_COUNT; i++){
painter.drawThickLine(m_points[i*2],m_points[i*2+1],LINE_WIDTH,LINE_WIDTH);
}
}
#ifdef SAVE_OUTPUT
dev->convertToQImage(m_colorSpace->profile()).save("drawThickLine.png");
#endif
}
void KisPainterBenchmark::benchmarkDrawQtLine()
{
KisPaintDeviceSP dev = new KisPaintDevice(m_colorSpace);
KoColor color(m_colorSpace);
color.fromQColor(Qt::white);
dev->clear();
dev->fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,color.data());
color.fromQColor(Qt::black);
KisPainter painter(dev);
painter.setPaintColor(color);
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
QPen pen;
pen.setWidth(LINE_WIDTH);
pen.setColor(Qt::white);
pen.setCapStyle(Qt::RoundCap);
QBENCHMARK{
for (int i = 0; i < LINE_COUNT; i++){
QPainterPath path;
path.moveTo(m_points[i*2]);
path.lineTo(m_points[i*2 + 1]);
painter.drawPainterPath(path, pen);
}
}
#ifdef SAVE_OUTPUT
dev->convertToQImage(m_colorSpace->profile(),0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT).save("drawQtLine.png");
#endif
}
void KisPainterBenchmark::benchmarkDrawScanLine()
{
KisPaintDeviceSP dev = new KisPaintDevice(m_colorSpace);
KoColor color(m_colorSpace);
color.fromQColor(Qt::white);
dev->clear();
dev->fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,color.data());
color.fromQColor(Qt::black);
KisPainter painter(dev);
painter.setPaintColor(color);
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
QBENCHMARK{
for (int i = 0; i < LINE_COUNT; i++){
painter.drawLine(m_points[i*2],m_points[i*2+1],LINE_WIDTH,true);
}
}
#ifdef SAVE_OUTPUT
dev->convertToQImage(m_colorSpace->profile(),0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT).save("drawScanLine.png");
#endif
}
QTEST_KDEMAIN(KisPainterBenchmark, NoGUI)
diff --git a/krita/benchmarks/kis_random_iterator_benchmark.cpp b/krita/benchmarks/kis_random_iterator_benchmark.cpp
index ec13152ff4b..63bd86c48bc 100644
--- a/krita/benchmarks/kis_random_iterator_benchmark.cpp
+++ b/krita/benchmarks/kis_random_iterator_benchmark.cpp
@@ -1,238 +1,238 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_random_iterator_benchmark.h"
#include "kis_benchmark_values.h"
#include "kis_paint_device.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <qtest_kde.h>
#include <kis_random_accessor_ng.h>
void KisRandomIteratorBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_color = new KoColor(m_colorSpace);
// some random color
m_color->fromQColor(QColor(0,120,250));
m_device->fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT,m_color->data());
}
void KisRandomIteratorBenchmark::cleanupTestCase()
{
delete m_color;
delete m_device;
}
void KisRandomIteratorBenchmark::benchmarkCreation()
{
QBENCHMARK{
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
}
}
void KisRandomIteratorBenchmark::benchmarkWriteBytes()
{
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo(j,i);
memcpy(it->rawData(), m_color->data(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkReadBytes()
{
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo(j,i);
memcpy(it->rawData(), m_color->data(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkConstReadBytes()
{
KisRandomConstAccessorSP it = m_device->createRandomConstAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo(j,i);
memcpy(m_color->data(), it->oldRawData(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkReadWriteBytes(){
KoColor c(m_colorSpace);
c.fromQColor(QColor(250,120,0));
KisPaintDevice dab(m_colorSpace);
dab.fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT, c.data());
KisRandomAccessorSP writeIterator = m_device->createRandomAccessorNG(0,0);
KisRandomConstAccessorSP constReadIterator = dab.createRandomConstAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
writeIterator->moveTo(j,i);
constReadIterator->moveTo(j,i);
memcpy(writeIterator->rawData(), constReadIterator->oldRawData(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkTotalRandom()
{
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
// set the seed so that we always go in the same permutation over the device
srand(123456);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo( rand() % TEST_IMAGE_WIDTH,
rand() % TEST_IMAGE_HEIGHT );
memcpy(it->rawData(), m_color->data(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkTotalRandomConst()
{
KisRandomConstAccessorSP it = m_device->createRandomConstAccessorNG(0,0);
// set the seed so that we always go in the same permutation over the device
srand(123456);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo( rand() % TEST_IMAGE_WIDTH,
rand() % TEST_IMAGE_HEIGHT );
memcpy(m_color->data(), it->oldRawData(), m_colorSpace->pixelSize());
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkNoMemCpy()
{
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo(j,i);
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkConstNoMemCpy()
{
KisRandomConstAccessorSP it = m_device->createRandomConstAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
it->moveTo(j,i);
}
}
}
}
#define TEST_AREA_WIDTH 256
#define TEST_AREA_HEIGHT 64
void KisRandomIteratorBenchmark::benchmarkTileByTileWrite()
{
int xTiles = TEST_IMAGE_WIDTH / TEST_AREA_WIDTH;
int yTiles = TEST_IMAGE_HEIGHT / TEST_AREA_HEIGHT;
int xUnprocessed = int(TEST_IMAGE_WIDTH) % int(TEST_AREA_WIDTH);
int yUnprocessed = int(TEST_IMAGE_HEIGHT) % int(TEST_AREA_HEIGHT);
if ((xUnprocessed) != 0 || (yUnprocessed) != 0)
{
- kWarning() << "There will be some unprocessed pixels! Test area differs from the image size";
+ dbgKrita << "There will be some unprocessed pixels! Test area differs from the image size";
}
KisRandomAccessorSP it = m_device->createRandomAccessorNG(0,0);
QBENCHMARK{
for (int yTile = 0; yTile < yTiles; yTile++){
for (int xTile = 0; xTile < xTiles; xTile++){
int x = xTile * TEST_AREA_WIDTH;
int y = yTile * TEST_AREA_HEIGHT;
for (int j = y; j< y+TEST_AREA_HEIGHT; j++ ){
for (int i = x; i < x+TEST_AREA_WIDTH ; i++){
it->moveTo(i,j);
memcpy(it->rawData(), m_color->data(), m_colorSpace->pixelSize());
}
}
}
}
}
}
void KisRandomIteratorBenchmark::benchmarkTwoIteratorsNoMemCpy()
{
KoColor c(m_colorSpace);
c.fromQColor(QColor(250,120,0));
KisPaintDevice dab(m_colorSpace);
dab.fill(0,0,TEST_IMAGE_WIDTH,TEST_IMAGE_HEIGHT, c.data());
KisRandomAccessorSP writeIterator = m_device->createRandomAccessorNG(0,0);
KisRandomConstAccessorSP constReadIterator = dab.createRandomConstAccessorNG(0,0);
QBENCHMARK{
for (int i = 0; i < TEST_IMAGE_HEIGHT; i++){
for (int j = 0; j < TEST_IMAGE_WIDTH; j++) {
writeIterator->moveTo(j,i);
constReadIterator->moveTo(j,i);
}
}
}
}
QTEST_KDEMAIN(KisRandomIteratorBenchmark, GUI)
diff --git a/krita/benchmarks/kis_stroke_benchmark.cpp b/krita/benchmarks/kis_stroke_benchmark.cpp
index 902b6be9b47..3d29b2a30c7 100644
--- a/krita/benchmarks/kis_stroke_benchmark.cpp
+++ b/krita/benchmarks/kis_stroke_benchmark.cpp
@@ -1,513 +1,513 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#if defined(_WIN32) || defined(_WIN64)
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
#include <qtest_kde.h>
#include "kis_stroke_benchmark.h"
#include "kis_benchmark_values.h"
#include "kis_paint_device.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_information.h>
#include <kis_paintop_preset.h>
#define GMP_IMAGE_WIDTH 3274
#define GMP_IMAGE_HEIGHT 2067
#include <kis_painter.h>
#include <kis_paintop_registry.h>
//#define SAVE_OUTPUT
static const int LINES = 20;
const QString OUTPUT_FORMAT = ".png";
void KisStrokeBenchmark::initTestCase()
{
m_dataPath = QString(FILES_DATA_DIR) + QDir::separator();
m_outputPath = QString(FILES_OUTPUT_DIR) + QDir::separator();
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_color = KoColor(m_colorSpace);
int width = TEST_IMAGE_WIDTH;
int height = TEST_IMAGE_HEIGHT;
m_image = new KisImage(0, width, height, m_colorSpace, "stroke sample image", false);
m_layer = new KisPaintLayer(m_image, "temporary for stroke sample", OPACITY_OPAQUE_U8, m_colorSpace);
m_painter = new KisPainter(m_layer->paintDevice());
m_painter->setPaintColor(KoColor(Qt::black, m_colorSpace));
// for bezier curve test
initCurvePoints(width, height);
// for the lines test
initLines(width,height);
}
void KisStrokeBenchmark::init()
{
KoColor white(m_colorSpace);
white.fromQColor(Qt::white);
m_layer->paintDevice()->fill(0,0, m_image->width(), m_image->height(),white.data());
}
void KisStrokeBenchmark::initCurvePoints(int width, int height)
{
QPointF p1(0 , 7.0 / 12.0 * height);
QPointF p2(1.0 / 2.0 * width , 7.0 / 12.0 * height);
QPointF p3(width - 4.0, height - 4.0);
m_c1 = QPointF(1.0 / 4.0 * width, height - 2.0);
m_c2 = QPointF(3.0 / 4.0 * width, 0);
m_pi1 = KisPaintInformation(p1, 0.0);
m_pi2 = KisPaintInformation(p2, 0.95);
m_pi3 = KisPaintInformation(p3, 0.0);
}
void KisStrokeBenchmark::initLines(int width, int height)
{
srand(12345678);
for (int i = 0; i < LINES; i++){
qreal sx = rand() / qreal(RAND_MAX - 1);
qreal sy = rand() / qreal(RAND_MAX - 1);
m_startPoints.append(QPointF(sx * width,sy * height));
qreal ex = rand() / qreal(RAND_MAX - 1);
qreal ey = rand() / qreal(RAND_MAX - 1);
m_endPoints.append(QPointF(ex * width,ey * height));
}
}
void KisStrokeBenchmark::cleanupTestCase()
{
delete m_painter;
}
void KisStrokeBenchmark::deformBrush()
{
QString presetFileName = "deform-default.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::deformBrushRL()
{
QString presetFileName = "deform-default.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::pixelbrush300px()
{
QString presetFileName = "autobrush_300px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::pixelbrush300pxRL()
{
QString presetFileName = "autobrush_300px.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayPixels()
{
QString presetFileName = "spray_wu_pixels1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayPixelsRL()
{
QString presetFileName = "spray_wu_pixels1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayTexture()
{
QString presetFileName = "spray_21_textures1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayTextureRL()
{
QString presetFileName = "spray_21_textures1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::spray30px21particles()
{
QString presetFileName = "spray_30px21rasterParticles.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::spray30px21particlesRL()
{
QString presetFileName = "spray_30px21rasterParticles.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::sprayPencil()
{
QString presetFileName = "spray_scaled2rasterParticles.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::sprayPencilRL()
{
QString presetFileName = "spray_scaled2rasterParticles.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::softbrushDefault30()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::softbrushCircle30()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkCircle(presetFileName);
}
void KisStrokeBenchmark::softbrushDefault30RL()
{
QString presetFileName = "softbrush_30px.kpp";
benchmarkRandomLines(presetFileName);}
void KisStrokeBenchmark::softbrushFullFeatures30()
{
QString presetFileName = "softbrush_30px_full.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::softbrushFullFeatures30RL()
{
QString presetFileName = "softbrush_30px_full.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30pxDefault()
{
QString presetFileName = "hairybrush_thesis30px1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30pxDefaultRL()
{
QString presetFileName = "hairybrush_thesis30px1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30pxAntiAlias()
{
QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30pxAntiAliasRL()
{
QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30px30density()
{
QString presetFileName = "hairybrush_thesis30px_density301.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30px30densityRL()
{
QString presetFileName = "hairybrush_thesis30px_density301.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::hairy30InkDepletion()
{
QString presetFileName = "hairy30inkDepletion1.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::hairy30InkDepletionRL()
{
QString presetFileName = "hairy30inkDepletion1.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::softbrushOpacity()
{
QString presetFileName = "softbrush_opacity1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::softbrushSoftness()
{
QString presetFileName = "softbrush_softness1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::dynabrush()
{
QString presetFileName = "dyna301.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::dynabrushRL()
{
QString presetFileName = "dyna301.kpp";
benchmarkRandomLines(presetFileName);
}
void KisStrokeBenchmark::experimental()
{
QString presetFileName = "experimental.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::experimentalCircle()
{
QString presetFileName = "experimental.kpp";
benchmarkCircle(presetFileName);
}
void KisStrokeBenchmark::colorsmudge()
{
QString presetFileName = "colorsmudge.kpp";
benchmarkStroke(presetFileName);
}
void KisStrokeBenchmark::colorsmudgeRL()
{
QString presetFileName = "colorsmudge.kpp";
benchmarkStroke(presetFileName);
}
/*
void KisStrokeBenchmark::predefinedBrush()
{
QString presetFileName = "deevad-slow-brush1.kpp";
benchmarkLine(presetFileName);
}
void KisStrokeBenchmark::predefinedBrushRL()
{
QString presetFileName = "deevad-slow-brush1.kpp";
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
preset->load();
preset->settings()->setNode(m_layer);
m_painter->setPaintOpPreset(preset, m_image);
sleep(3);
QBENCHMARK{
for (int i = 0; i < LINES; i++){
KisPaintInformation pi1(m_startPoints[i], 0.0);
KisPaintInformation pi2(m_endPoints[i], 1.0);
m_painter->paintLine(pi1, pi2);
}
}
//m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT);
}
*/
inline void KisStrokeBenchmark::benchmarkLine(QString presetFileName)
{
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
preset->load();
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QPointF startPoint(0.10 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT);
QPointF endPoint(0.90 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT);
KisDistanceInformation currentDistance;
KisPaintInformation pi1(startPoint, 0.0);
KisPaintInformation pi2(endPoint, 1.0);
QBENCHMARK{
m_painter->paintLine(pi1, pi2, &currentDistance);
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_line" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkCircle(QString presetFileName)
{
- qDebug() << "(circle)preset : " << presetFileName;
+ dbgKrita << "(circle)preset : " << presetFileName;
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
if (!preset->load()){
- qDebug() << "Preset was not loaded";
+ dbgKrita << "Preset was not loaded";
return;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
qreal radius = 300;
qreal randomOffset = 300 * 0.4;
int rounds = 20;
int steps = 20;
qreal step = 1.0 / steps;
QPointF center(m_image->width() * 0.5, m_image->height() * 0.5);
QPointF first(center.x()+radius,center.y());
srand48(0);
for (int k = 0; k < rounds; k++){
KisDistanceInformation currentDistance;
m_painter->paintLine(center, first, &currentDistance);
QPointF prev = first;
for (int i = 1; i < steps; i++) {
qreal cx = cos(i * step * 2 * M_PI);
qreal cy = sin(i * step * 2 * M_PI);
cx *= (radius + drand48() * randomOffset);
cy *= (radius + drand48() * randomOffset);
cx += center.x();
cy += center.y();
m_painter->paintLine(prev, QPointF(cx,cy), &currentDistance);
prev = QPointF(cx,cy);
}
m_painter->paintLine(prev, first, &currentDistance);
}
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_circle" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkRandomLines(QString presetFileName)
{
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
bool loadedOk = preset->load();
if (!loadedOk){
- qDebug() << "The preset was not loaded correctly. Done.";
+ dbgKrita << "The preset was not loaded correctly. Done.";
return;
}else{
- qDebug() << "preset : " << presetFileName;
+ dbgKrita << "preset : " << presetFileName;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
KisDistanceInformation currentDistance;
for (int i = 0; i < LINES; i++){
KisPaintInformation pi1(m_startPoints[i], 0.0);
KisPaintInformation pi2(m_endPoints[i], 1.0);
m_painter->paintLine(pi1, pi2, &currentDistance);
}
}
#ifdef SAVE_OUTPUT
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT);
#endif
}
void KisStrokeBenchmark::benchmarkStroke(QString presetFileName)
{
KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName);
bool loadedOk = preset->load();
if (!loadedOk){
- qDebug() << "The preset was not loaded correctly. Done.";
+ dbgKrita << "The preset was not loaded correctly. Done.";
return;
} else {
- qDebug() << "preset : " << presetFileName;
+ dbgKrita << "preset : " << presetFileName;
}
m_painter->setPaintOpPreset(preset, m_layer, m_image);
QBENCHMARK{
KisDistanceInformation currentDistance;
m_painter->paintBezierCurve(m_pi1, m_c1, m_c1, m_pi2, &currentDistance);
m_painter->paintBezierCurve(m_pi2, m_c2, m_c2, m_pi3, &currentDistance);
}
#ifdef SAVE_OUTPUT
- qDebug() << "Saving output " << m_outputPath + presetFileName + ".png";
+ dbgKrita << "Saving output " << m_outputPath + presetFileName + ".png";
m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + OUTPUT_FORMAT);
#endif
}
static const int COUNT = 1000000;
void KisStrokeBenchmark::benchmarkRand48()
{
QBENCHMARK
{
for (int i = 0 ; i < COUNT; i++){
drand48();
}
}
}
void KisStrokeBenchmark::benchmarkRand()
{
float j;
QBENCHMARK{
for (int i = 0 ; i < COUNT; i++){
j = rand() / (float)RAND_MAX;
}
}
Q_UNUSED(j);
}
QTEST_KDEMAIN(KisStrokeBenchmark, GUI)
diff --git a/krita/crashreporter/main.cpp b/krita/crashreporter/main.cpp
index 6050fb28b12..282816991d9 100644
--- a/krita/crashreporter/main.cpp
+++ b/krita/crashreporter/main.cpp
@@ -1,87 +1,87 @@
/* This file is part of the KDE project
Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QTextStream>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <klocale.h>
#include <kglobal.h>
#include <kapplication.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <calligraversion.h>
#include <calligragitversion.h>
#include "mainwindow.h"
int main( int argc, char **argv )
{
QString calligraVersion(CALLIGRA_VERSION_STRING);
QString version;
KLocalizedString::setApplicationDomain( "crashhandler" );
#ifdef CALLIGRA_GIT_SHA1_STRING
QString gitVersion(CALLIGRA_GIT_SHA1_STRING);
version = QString("%1 (git %2)").arg(calligraVersion).arg(gitVersion).toLatin1();
#else
version = calligraVersion;
#endif
KAboutData aboutData("krita", 0,
ki18n("Krita Crash Reporter"),
version.toLatin1(),
ki18n("Digital Painting for Artists"),
KAboutData::License_GPL,
ki18n("(c) 2014 The Krita team.\n"),
KLocalizedString(),
"http://www.krita.org",
"submit@bugs.kde.org");
aboutData.addAuthor(ki18n("Boudewijn Rempt"),
ki18n("Maintainer"),
"boud@valdyas.org", "http://www.valdyas.org/fading/index.cgi");
KCmdLineArgs::init(argc, argv, &aboutData);
KCmdLineOptions options;
options.add("+[arg1]");
options.add("+[arg2]");
KCmdLineArgs::addCmdLineOptions( options );
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
KApplication app;
QStringList arguments = args->allArguments();
// Something went wrong, whatever: we restart Krita
if (arguments.size() != 3) {
MainWindow mw("", "");
mw.restart();
}
QString dumpPath = arguments[1];
QString dumpId = arguments[2];
MainWindow mw(dumpPath, dumpId);
mw.show();
return app.exec();
}
diff --git a/krita/crashreporter/mainwindow.cpp b/krita/crashreporter/mainwindow.cpp
index 1407cd7acf1..f2a1ca9340c 100644
--- a/krita/crashreporter/mainwindow.cpp
+++ b/krita/crashreporter/mainwindow.cpp
@@ -1,402 +1,402 @@
/*
* Copyright (c) 2008-2009 Hyves (Startphone Ltd.)
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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
*/
#include "mainwindow.h"
#include <QtCore>
#include <QtGui>
#include <QtNetwork>
#include <kglobal.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <calligraversion.h>
#include <calligragitversion.h>
#include <cstdlib>
#include <KoConfig.h>
#ifdef Q_OS_MAC
#include <windows.h>
#endif
//#ifdef Q_OS_MAC
//#include <cstring>
//#include <windows.h>
//#include <shellapi.h>
///**
// * Native Win32 method for starting a process. This is required in order to
// * launch the installer with User Account Control enabled.
// *
// * @param path Path to the process to start.
// * @param parameters Parameters for the process.
// * @return @c true if the process is started successfully, @c false otherwise.
// */
//bool startProcess(LPCWSTR path, char *parameters = 0) {
// Q_ASSERT(path != 0);
// SHELLEXECUTEINFO info;
// memset(&info, '\0', sizeof(info));
// info.cbSize = sizeof(info);
// info.fMask = 0;
// info.hwnd = 0;
// info.lpVerb = TEXT("open");
// info.lpFile = path;
// info.lpParameters = (LPCWSTR)parameters;
// info.lpDirectory = 0;
// info.nShow = SW_SHOWNORMAL;
// return ShellExecuteEx(&info);
//}
//void doRestart(bool resetConfig)
//{
// if (!startProcess(QString("krita").toStdWString().data())) {
// QMessageBox::warning(0, i18nc("@title:window", "Krita"),
// i18n("Could not restart %1. Please try to restart %1 manually.").arg("krita"));
// }
//}
//#else // Q_OS_MAC
void doRestart(MainWindow* mainWindow, bool resetConfig)
{
if (resetConfig) {
{
QString appdata = qgetenv("APPDATA");
QDir inputDir(appdata + "/krita/share/apps/krita/input/");
foreach(QString entry, inputDir.entryList(QStringList("*.profile"))) {
inputDir.remove(entry);
}
QDir configDir(appdata + "/krita/share/config/");
configDir.remove("kritarc");
}
{
QString appdata = qgetenv("LOCALAPPDATA");
QDir inputDir(appdata + "/krita/share/apps/krita/input/");
foreach(QString entry, inputDir.entryList(QStringList("*.profile"))) {
inputDir.remove(entry);
}
QDir configDir(appdata + "/krita/share/config/");
configDir.remove("kritarc");
}
{
QDir inputDir(KGlobal::dirs()->saveLocation("data", "krita/input/"));
foreach(QString entry, inputDir.entryList(QStringList("*.profile"))) {
inputDir.remove(entry);
}
QDir configDir(KGlobal::dirs()->saveLocation("config"));
configDir.remove("kritarc");
}
}
QString restartCommand;
#ifdef Q_WS_MAC
QDir bundleDir(qApp->applicationDirPath());
bundleDir.cdUp();
bundleDir.cdUp();
bundleDir.cdUp();
restartCommand = QString("open \"") + QString(bundleDir.absolutePath() + "/krita.app\"");
#endif
#ifdef Q_OS_MAC
restartCommand = qApp->applicationDirPath().replace(' ', "\\ ") + "/krita.exe \"";
#endif
#ifdef HAVE_X11
restartCommand = "sh -c \"" + qApp->applicationDirPath().replace(' ', "\\ ") + "/krita \"";
#endif
- qDebug() << "restartCommand" << restartCommand;
+ dbgKrita << "restartCommand" << restartCommand;
QProcess restartProcess;
if (!restartProcess.startDetached(restartCommand)) {
QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita"),
i18n("Could not restart Krita. Please try to restart manually."));
}
}
//#endif // Q_OS_MAC
#ifdef Q_WS_MAC
QString platformToStringMac(QSysInfo::MacVersion version)
{
switch(version) {
case QSysInfo::MV_9:
return "MacOS 9";
case QSysInfo::MV_10_0:
return "OSX 10.0 (cheetah)";
case QSysInfo::MV_10_1:
return "OSX 10.1 (puma)";
case QSysInfo::MV_10_2:
return "OSX 10.2 (jaguar)";
case QSysInfo::MV_10_3:
return "OSX 10.3 (panther)";
case QSysInfo::MV_10_4:
return "OSX 10.4 (tiger)";
case QSysInfo::MV_10_5:
return "OSX 10.5 (leopard)";
case QSysInfo::MV_10_6:
return "OSX 10.6 (snow leopard)";
case QSysInfo::MV_10_7:
return "OSX 10.6 (Lion)";
case QSysInfo::MV_10_8:
return "OSX 10.6 (Mountain Lion)";
case QSysInfo::MV_Unknown:
default:
return "Unknown OSX version";
};
}
#endif
#ifdef Q_OS_MAC
QString platformToStringWin(QSysInfo::WinVersion version)
{
switch(version) {
case QSysInfo::WV_32s:
return "Windows 3.1 with win32s";
case QSysInfo::WV_95:
return "Windows 95";
case QSysInfo::WV_98:
return "Windows 98";
case QSysInfo::WV_Me:
return "Windows Me";
case QSysInfo::WV_NT:
return "Windows NT";
case QSysInfo::WV_2000:
return "Windows 2000";
case QSysInfo::WV_XP:
return "Windows XP";
case QSysInfo::WV_2003:
return "Windows 2003";
case QSysInfo::WV_VISTA:
return "Windows Vista";
case QSysInfo::WV_WINDOWS7:
return "Windows 7";
case QSysInfo::WV_WINDOWS8:
return "Windows 8";
case QSysInfo::WV_WINDOWS8_1:
return "Windows 8.1";
default:
return "Unknown Windows version";
};
}
#endif
struct MainWindow::Private {
QString dumpPath;
QString id;
QNetworkAccessManager *networkAccessManager;
bool doRestart;
bool uploadStarted;
Private() :
doRestart(true),
uploadStarted(false) {
}
};
MainWindow::MainWindow(const QString &dumpPath, const QString &id, QWidget *parent)
: QWidget(parent)
, m_d(new Private())
{
setupUi(this);
progressBar->hide();
lblKiki->setPixmap(QPixmap(KGlobal::dirs()->findResource("data", "krita/pics/KikiNurse_sm.png")));
setWindowFlags(Qt::WindowStaysOnTopHint | windowFlags());
m_d->networkAccessManager = new QNetworkAccessManager(this);
connect(m_d->networkAccessManager, SIGNAL(finished(QNetworkReply *)), SLOT(uploadDone(QNetworkReply *)));
connect(bnRestart, SIGNAL(clicked()), this, SLOT(restart()));
connect(bnClose, SIGNAL(clicked()), this, SLOT(close()));
m_d->dumpPath = dumpPath;
m_d->id = id;
}
MainWindow::~MainWindow()
{
delete m_d;
}
void MainWindow::restart()
{
m_d->doRestart = true;
if (chkAllowUpload->isChecked()) {
startUpload();
}
else {
doRestart(this, chkRemoveSettings->isChecked());
qApp->quit();
}
}
void MainWindow::close()
{
m_d->doRestart = false;
if (!m_d->uploadStarted && chkAllowUpload->isChecked()) {
startUpload();
}
else {
qApp->quit();
}
}
void MainWindow::startUpload()
{
bnRestart->setEnabled(false);
bnClose->setEnabled(false);
progressBar->show();
m_d->uploadStarted = true;
// Upload minidump
QNetworkRequest request(QUrl("http://krita-breakpad.kogmbh.net:1127/post"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=9876543210");
QString boundary = "--9876543210";
typedef QPair<QByteArray, QByteArray> Field;
QList<Field> fields;
QString calligraVersion(CALLIGRA_VERSION_STRING);
QString version;
#ifdef CALLIGRA_GIT_SHA1_STRING
QString gitVersion(CALLIGRA_GIT_SHA1_STRING);
version = QString("%1-%2").arg(calligraVersion).arg(gitVersion).toLatin1();
#else
version = calligraVersion;
#endif
fields << Field("BuildID", CALLIGRA_GIT_SHA1_STRING)
<< Field("ProductName", "krita")
<< Field("Version", version.toLatin1())
<< Field("Vendor", "KO GmbH")
<< Field("Email", txtEmail->text().toLatin1())
<< Field("timestamp", QByteArray::number(QDateTime::currentDateTime().toTime_t()));
#ifdef Q_OS_MAC
QString platform = platformToStringWin(QSysInfo::WindowsVersion);
#ifdef ENV32BIT
platform += "_x86";
#else
platform += "_x64";
#endif
fields << Field("Platform", platform.toLatin1());
#endif
#ifdef HAVE_X11
fields << Field("Platform", "Linux/X11");
#endif
#ifdef Q_WS_MAC
fields << Field("Platform", platformToStringMac(QSysInfo::MacintoshVersion).toLatin1());
#endif
QFile f(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt");
- qDebug() << KGlobal::dirs()->saveLocation("config") + "/krita-opengl.txt" << f.exists();
+ dbgKrita << KGlobal::dirs()->saveLocation("config") + "/krita-opengl.txt" << f.exists();
if (f.exists()) {
f.open(QFile::ReadOnly);
fields << Field("OpenGL", f.readAll());
}
QString body;
foreach(Field const field, fields) {
body += boundary + "\r\n";
body += "Content-Disposition: form-data; name=\"" + field.first + "\"\r\n\r\n";
body += field.second + "\r\n";
}
body += boundary + "\r\n";
// add minidump file
QString dumpfile = m_d->dumpPath + "/" + m_d->id + ".dmp";
- qDebug() << "dumpfile" << dumpfile;
+ dbgKrita << "dumpfile" << dumpfile;
body += "Content-Disposition: form-data; name=\"upload_file_minidump\"; filename=\""
+ QFileInfo(dumpfile).fileName().toLatin1() + "\"\r\n";
body += "Content-Type: application/octet-stream\r\n\r\n";
QFile file(dumpfile);
if (file.exists()) {
file.open(QFile::ReadOnly);
QByteArray ba = file.readAll();
body += ba.toBase64();
file.remove();
}
body += "\r\n";
// add description
body += boundary + "\r\n";
body += "Content-Disposition: form-data; name=\"description\"\r\n";
body += "\r\n";
body += txtDescription->toPlainText();
body += "\r\n";
body += boundary + "--" + "\r\n";
QFile report(QDir::homePath() + "/krita-" + m_d->id + ".report");
report.open(QFile::WriteOnly);
report.write(body.toLatin1());
report.close();
QNetworkReply *reply = m_d->networkAccessManager->post(request, body.toLatin1());
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(uploadError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64,qint64)));
}
void MainWindow::uploadDone(QNetworkReply *reply)
{
- qDebug() << "updloadDone";
+ dbgKrita << "updloadDone";
if (reply && reply->error() != QNetworkReply::NoError) {
- qCritical() << "uploadDone: Error uploading crash report: " << reply->errorString();
+ errKrita << "uploadDone: Error uploading crash report: " << reply->errorString();
}
if (m_d->doRestart) {
doRestart(this, chkRemoveSettings->isChecked());
}
qApp->quit();
}
void MainWindow::uploadProgress(qint64 received, qint64 total)
{
- qDebug() << "updloadProgress";
+ dbgKrita << "updloadProgress";
progressBar->setMaximum(total);
progressBar->setValue(received);
}
void MainWindow::uploadError(QNetworkReply::NetworkError error)
{
- qDebug() << "updloadError" << error;
+ dbgKrita << "updloadError" << error;
// Fake success...
progressBar->setRange(0, 100);
progressBar->setValue(100);
- qCritical() << "UploadError: Error uploading crash report: " << error;
+ errKrita << "UploadError: Error uploading crash report: " << error;
uploadDone(0);
}
diff --git a/krita/gemini/MainWindow.cpp b/krita/gemini/MainWindow.cpp
index 2f4c8ae9445..b93c8d1ab93 100644
--- a/krita/gemini/MainWindow.cpp
+++ b/krita/gemini/MainWindow.cpp
@@ -1,791 +1,791 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
* Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt <boud@kogmbh.com>
* Copyright (C) 2013 Dan Leinir Turthra Jensen <admin@leinir.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "MainWindow.h"
#include "desktopviewproxy.h"
#include "ViewModeSwitchEvent.h"
#include "opengl/kis_opengl.h"
#include <QApplication>
#include <QResizeEvent>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QGraphicsObject>
#include <QDir>
#include <QFile>
#include <QToolButton>
#include <QFileInfo>
#include <QGLWidget>
#include <QDesktopServices>
#include <kurl.h>
#include <kstandarddirs.h>
#include <kactioncollection.h>
#include <QMessageBox>
#include <kmenubar.h>
#include <kxmlguifactory.h>
#include <kdialog.h>
#include <KoCanvasBase.h>
#include <KisMainWindow.h>
#include <KoGlobal.h>
#include <KoDocumentInfo.h>
#include <KoAbstractGradient.h>
#include <KoZoomController.h>
#include <KoFileDialog.h>
#include <KisDocumentEntry.h>
#include <KisImportExportManager.h>
#include <KoToolManager.h>
#include <KoIcon.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
#include "kis_paintop.h"
#include "kis_paintop_registry.h"
#include <kis_paintop_preset.h>
#include <KoPattern.h>
#include <kis_config.h>
#include <kis_factory2.h>
#include <KisDocument.h>
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <kis_canvas_controller.h>
#include "sketch/SketchDeclarativeView.h"
#include "sketch/RecentFileManager.h"
#include "sketch/DocumentManager.h"
#include "sketch/QmlGlobalEngine.h"
#include "sketch/Settings.h"
#ifdef Q_OS_WIN
// Slate mode/docked detection stuff
#include <shellapi.h>
#define SM_CONVERTIBLESLATEMODE 0x2003
#define SM_SYSTEMDOCKED 0x2004
#endif
class MainWindow::Private
{
public:
Private(MainWindow* qq)
: q(qq)
, allowClose(true)
, sketchView(0)
, desktopWindow(0)
, currentView(0)
, desktopCursorStyle(CURSOR_STYLE_NO_CURSOR)
, slateMode(false)
, docked(false)
, sketchKisView(0)
, desktopKisView(0)
, desktopViewProxy(0)
, forceFullScreen(false)
, forceDesktop(false)
, forceSketch(false)
, temporaryFile(false)
, syncObject(0)
, toDesktop(0)
, toSketch(0)
, switcher(0)
{
#ifdef Q_OS_WIN
// slateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
// docked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0);
#endif
centerer = new QTimer(q);
centerer->setInterval(10);
centerer->setSingleShot(true);
connect(centerer, SIGNAL(timeout()), q, SLOT(adjustZoomOnDocumentChangedAndStuff()));
}
MainWindow* q;
bool allowClose;
SketchDeclarativeView* sketchView;
KisMainWindow* desktopWindow;
QObject* currentView;
CursorStyle desktopCursorStyle;
bool slateMode;
bool docked;
QString currentSketchPage;
KisView* sketchKisView;
KisView* desktopKisView;
DesktopViewProxy* desktopViewProxy;
bool forceFullScreen;
bool forceDesktop;
bool forceSketch;
bool temporaryFile;
ViewModeSynchronisationObject* syncObject;
QTimer* centerer;
KAction* toDesktop;
KAction* toSketch;
QToolButton* switcher;
void initSketchView(QObject* parent)
{
sketchView = new SketchDeclarativeView();
QmlGlobalEngine::instance()->setEngine(sketchView->engine());
sketchView->engine()->rootContext()->setContextProperty("mainWindow", parent);
#ifdef Q_OS_WIN
QDir appdir(qApp->applicationDirPath());
// Corrects for mismatched case errors in path (qtdeclarative fails to load)
wchar_t buffer[1024];
QString absolute = appdir.absolutePath();
DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
rv = ::GetLongPathName(buffer, buffer, 1024);
QString correctedPath((QChar *)buffer);
appdir.setPath(correctedPath);
// for now, the app in bin/ and we still use the env.bat script
appdir.cdUp();
sketchView->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports");
sketchView->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports");
QString mainqml = appdir.canonicalPath() + "/share/apps/kritagemini/kritagemini.qml";
#else
sketchView->engine()->addImportPath(KGlobal::dirs()->findDirs("lib", "calligra/imports").value(0));
QString mainqml = KGlobal::dirs()->findResource("data", "kritagemini/kritagemini.qml");
#endif
Q_ASSERT(QFile::exists(mainqml));
if (!QFile::exists(mainqml)) {
QMessageBox::warning(0, i18nc("@title:window", "Krita: No QML Found"), i18n("%1 doesn't exist.", mainqml));
}
QFileInfo fi(mainqml);
sketchView->setSource(QUrl::fromLocalFile(fi.canonicalFilePath()));
sketchView->setResizeMode( QDeclarativeView::SizeRootObjectToView );
if (sketchView->errors().count() > 0) {
foreach(const QDeclarativeError &error, sketchView->errors()) {
- qDebug() << error.toString();
+ dbgKrita << error.toString();
}
}
toDesktop = new KAction(q);
toDesktop->setEnabled(false);
toDesktop->setText(tr("Switch to Desktop"));
// useful for monkey-testing to crash...
//toDesktop->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
//q->addAction(toDesktop);
//connect(toDesktop, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchDesktopForced()));
connect(toDesktop, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToDesktop()));
sketchView->engine()->rootContext()->setContextProperty("switchToDesktopAction", toDesktop);
}
void initDesktopView()
{
// Tell the iconloader about share/apps/calligra/icons
KIconLoader::global()->addAppDir("calligra");
// Initialize all Calligra directories etc.
KoGlobal::initialize();
// The default theme is not what we want for Gemini
KConfigGroup group(KGlobal::config(), "theme");
if(group.readEntry("Theme", "no-theme-is-set") == QLatin1String("no-theme-is-set")) {
group.writeEntry("Theme", "Krita-dark");
}
desktopWindow = new KisMainWindow();
toSketch = new KAction(desktopWindow);
toSketch->setEnabled(false);
toSketch->setText(tr("Switch to Sketch"));
toSketch->setIcon(QIcon::fromTheme("system-reboot"));
toSketch->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S);
//connect(toSketch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchSketchForced()));
connect(toSketch, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), q, SLOT(switchToSketch()));
desktopWindow->actionCollection()->addAction("SwitchToSketchView", toSketch);
switcher = new QToolButton();
switcher->setEnabled(false);
switcher->setText(tr("Switch to Sketch"));
switcher->setIcon(QIcon::fromTheme("system-reboot"));
switcher->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
//connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchDesktopForced()));
connect(switcher, SIGNAL(clicked(bool)), q, SLOT(switchToSketch()));
desktopWindow->menuBar()->setCornerWidget(switcher);
// DesktopViewProxy connects itself up to everything appropriate on construction,
// and destroys itself again when the view is removed
desktopViewProxy = new DesktopViewProxy(q, desktopWindow);
connect(desktopViewProxy, SIGNAL(documentSaved()), q, SIGNAL(documentSaved()));
connect(desktopViewProxy, SIGNAL(documentSaved()), q, SLOT(resetWindowTitle()));
}
void notifySlateModeChange();
void notifyDockingModeChange();
bool queryClose();
};
MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags )
: QMainWindow( parent, flags ), d( new Private(this) )
{
qApp->setActiveWindow( this );
setWindowTitle(i18n("Krita Gemini"));
setWindowIcon(koIcon("kritagemini"));
// Load filters and other plugins in the gui thread
Q_UNUSED(KisFilterRegistry::instance());
Q_UNUSED(KisPaintOpRegistry::instance());
KisConfig cfg;
// Store the current setting before we do "things", and heuristic our way to a reasonable
// default if it's no cursor (that's most likely due to a broken config)
if (cfg.newCursorStyle() != CURSOR_STYLE_NO_CURSOR)
d->desktopCursorStyle = cfg.newCursorStyle();
cfg.setNewCursorStyle(CURSOR_STYLE_NO_CURSOR);
cfg.setUseOpenGL(true);
foreach(QString fileName, fileNames) {
DocumentManager::instance()->recentFileManager()->addRecent( QDir::current().absoluteFilePath( fileName ) );
}
connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(documentChanged()));
connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(resetWindowTitle()));
connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(resetWindowTitle()));
d->initSketchView(this);
// Set the initial view to sketch... because reasons.
// Really, this allows us to show the pleasant welcome screen from Sketch
switchToSketch();
if(!fileNames.isEmpty()) {
//It feels a little hacky, but call a QML function to open files.
//This saves a lot of hassle required to change state for loading dialogs etc.
QMetaObject::invokeMethod(d->sketchView->rootObject(), "openFile", Q_ARG(QVariant, fileNames.at(0)));
}
}
void MainWindow::resetWindowTitle()
{
KUrl url(DocumentManager::instance()->settingsManager()->currentFile());
QString fileName = url.fileName();
if(url.protocol() == "temp")
fileName = i18n("Untitled");
KDialog::CaptionFlags flags = KDialog::HIGCompliantCaption;
KisDocument* document = DocumentManager::instance()->document();
if (document && document->isModified() ) {
flags |= KDialog::ModifiedCaption;
}
setWindowTitle( KDialog::makeStandardCaption(fileName, this, flags) );
}
void MainWindow::switchDesktopForced()
{
if (d->slateMode)
d->forceDesktop = true;
d->forceSketch = false;
}
void MainWindow::switchSketchForced()
{
if (!d->slateMode)
d->forceSketch = true;
d->forceDesktop = false;
}
void MainWindow::switchToSketch()
{
if (d->toSketch)
{
d->toSketch->setEnabled(false);
d->switcher->setEnabled(false);
}
d->syncObject = new ViewModeSynchronisationObject;
KisViewManager* view = 0;
KisConfig cfg;
if (d->desktopWindow && centralWidget() == d->desktopWindow) {
d->desktopCursorStyle = cfg.newCursorStyle();
view = qobject_cast<KisViewManager*>(d->desktopWindow->activeView());
//Notify the view we are switching away from that we are about to switch away from it
//giving it the possibility to set up the synchronisation object.
ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, view, d->sketchView, d->syncObject);
QApplication::sendEvent(view, &aboutToSwitchEvent);
d->desktopWindow->setParent(0);
}
setCentralWidget(d->sketchView);
if (d->slateMode) {
setWindowState(windowState() | Qt::WindowFullScreen);
if (d->syncObject->initialized)
QTimer::singleShot(50, this, SLOT(sketchChange()));
}
else
QTimer::singleShot(50, this, SLOT(sketchChange()));
if (view && view->document()) {
view->document()->setSaveInBatchMode(true);
}
}
void MainWindow::sketchChange()
{
if (centralWidget() != d->sketchView || !d->syncObject)
return;
if (d->desktopWindow)
{
if (!d->sketchKisView || !d->sketchView->canvasWidget())
{
QTimer::singleShot(100, this, SLOT(sketchChange()));
return;
}
qApp->processEvents();
KisViewManager* view = qobject_cast<KisViewManager*>(d->desktopWindow->activeView());
//Notify the new view that we just switched to it, passing our synchronisation object
//so it can use those values to sync with the old view.
ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToSketchModeEvent, view, d->sketchView, d->syncObject);
QApplication::sendEvent(d->sketchView, &switchedEvent);
d->syncObject = 0;
qApp->processEvents();
KisConfig cfg;
cfg.setNewCursorStyle(CURSOR_STYLE_NO_CURSOR);
emit switchedToSketch();
}
if (d->toDesktop)
{
qApp->processEvents();
d->toDesktop->setEnabled(true);
}
}
void MainWindow::switchToDesktop(bool justLoaded)
{
if (d->toDesktop)
d->toDesktop->setEnabled(false);
ViewModeSynchronisationObject* syncObject = new ViewModeSynchronisationObject;
KisViewManager* view = 0;
if (d->desktopWindow) {
view = qobject_cast<KisViewManager*>(d->desktopWindow->activeView());
}
//Notify the view we are switching away from that we are about to switch away from it
//giving it the possibility to set up the synchronisation object.
ViewModeSwitchEvent aboutToSwitchEvent(ViewModeSwitchEvent::AboutToSwitchViewModeEvent, d->sketchView, view, syncObject);
QApplication::sendEvent(d->sketchView, &aboutToSwitchEvent);
qApp->processEvents();
if (d->currentSketchPage == "MainPage")
{
d->sketchView->setParent(0);
setCentralWidget(d->desktopWindow);
}
if (!d->forceFullScreen) {
setWindowState(windowState() & ~Qt::WindowFullScreen);
}
if (view) {
//Notify the new view that we just switched to it, passing our synchronisation object
//so it can use those values to sync with the old view.
ViewModeSwitchEvent switchedEvent(ViewModeSwitchEvent::SwitchedToDesktopModeEvent, d->sketchView, view, syncObject);
QApplication::sendEvent(view, &switchedEvent);
KisConfig cfg;
cfg.setNewCursorStyle(d->desktopCursorStyle);
}
if (d->toSketch && !justLoaded)
{
qApp->processEvents();
d->toSketch->setEnabled(true);
d->switcher->setEnabled(true);
}
if (view && view->document()) {
view->document()->setSaveInBatchMode(false);
}
}
void MainWindow::adjustZoomOnDocumentChangedAndStuff()
{
if (d->desktopWindow && centralWidget() == d->desktopWindow) {
KisView* view = qobject_cast<KisView*>(d->desktopWindow->activeView());
// We have to set the focus on the view here, otherwise the toolmanager is unaware of which
// canvas should be handled.
view->canvasController()->setFocus();
view->setFocus();
QPoint center = view->rect().center();
view->canvasController()->zoomRelativeToPoint(center, 0.9);
qApp->processEvents();
d->toSketch->setEnabled(true);
d->switcher->setEnabled(true);
}
else if (d->sketchKisView && centralWidget() == d->sketchView) {
qApp->processEvents();
d->sketchKisView->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0);
qApp->processEvents();
QPoint center = d->sketchKisView->rect().center();
d->sketchKisView->canvasController()->zoomRelativeToPoint(center, 0.9);
qApp->processEvents();
d->toDesktop->setEnabled(true);
}
// Ensure that we do, in fact, have the brush tool selected on the currently active canvas
KoToolManager::instance()->switchToolRequested( "InteractionTool" );
qApp->processEvents();
KoToolManager::instance()->switchToolRequested( "KritaShape/KisToolBrush" );
}
void MainWindow::documentChanged()
{
if (d->desktopWindow) {
d->desktopWindow->setNoCleanup(true);
d->desktopWindow->deleteLater();
d->desktopWindow = 0;
}
d->initDesktopView();
//d->desktopWindow->setRootDocument(DocumentManager::instance()->document(), DocumentManager::instance()->part(), false);
qApp->processEvents();
d->desktopKisView = qobject_cast<KisView*>(d->desktopWindow->activeView());
//d->desktopKisView->setQtMainWindow(d->desktopWindow);
// Define new actions here
KXMLGUIFactory* factory = d->desktopWindow->factory();
factory->removeClient(d->desktopWindow);
factory->addClient(d->desktopWindow);
d->desktopViewProxy->documentChanged();
connect(d->desktopKisView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start()));
connect(d->desktopKisView, SIGNAL(sigSavingFinished()), this, SLOT(resetWindowTitle()));
if (d->desktopKisView && d->desktopKisView->canvasBase() && d->desktopKisView->canvasBase()->resourceManager()) {
connect(d->desktopKisView->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)),
this, SLOT(resourceChanged(int, const QVariant&)));
}
if (!d->forceSketch && !d->slateMode)
switchToDesktop(true);
}
bool MainWindow::allowClose() const
{
return d->allowClose;
}
void MainWindow::setAllowClose(bool allow)
{
d->allowClose = allow;
}
bool MainWindow::slateMode() const
{
return d->slateMode;
}
void MainWindow::setSlateMode(bool newValue)
{
d->slateMode = newValue;
}
QString MainWindow::currentSketchPage() const
{
return d->currentSketchPage;
}
void MainWindow::setCurrentSketchPage(QString newPage)
{
d->currentSketchPage = newPage;
emit currentSketchPageChanged();
if (newPage == "MainPage")
{
if (!d->forceSketch && !d->slateMode)
{
// Just loaded to desktop, do nothing
}
else
{
//QTimer::singleShot(3000, this, SLOT(adjustZoomOnDocumentChangedAndStuff()));
}
}
}
bool MainWindow::temporaryFile() const
{
return d->temporaryFile;
}
void MainWindow::setTemporaryFile(bool newValue)
{
d->temporaryFile = newValue;
emit temporaryFileChanged();
}
QString MainWindow::openImage()
{
KoFileDialog dialog(this, KoFileDialog::OpenFile, "OpenDocument");
dialog.setCaption(i18n("Open Document"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
KisDocumentEntry entry = KisDocumentEntry::queryByMimeType("application/x-krita");
KService::Ptr service = entry.service();
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import, service->property("X-KDE-ExtraNativeMimeTypes").toStringList()));
dialog.setHideNameFilterDetailsOption();
return dialog.url();
}
void MainWindow::resourceChanged(int key, const QVariant& v)
{
Q_UNUSED(key);
if(centralWidget() == d->sketchView)
return;
KisPaintOpPresetSP preset = v.value<KisPaintOpPresetSP>();
if(preset && d->sketchKisView != 0) {
KisPaintOpPresetSP clone = preset;
d->sketchKisView->resourceProvider()->setPaintOpPreset(clone);
}
}
void MainWindow::resourceChangedSketch(int key, const QVariant& v)
{
Q_UNUSED(key);
if(centralWidget() == d->desktopWindow)
return;
KisPaintOpPresetSP preset = v.value<KisPaintOpPresetSP>();
if(preset && d->desktopKisView != 0) {
KisPaintOpPresetSP clone = preset;
d->desktopKisView->resourceProvider()->setPaintOpPreset(clone);
}
}
QObject* MainWindow::sketchKisView() const
{
return d->sketchKisView;
}
void MainWindow::setSketchKisView(QObject* newView)
{
if (d->sketchKisView) {
d->sketchKisView->disconnect(this);
d->sketchKisView->canvasBase()->resourceManager()->disconnect(this);
}
if (d->sketchKisView != newView)
{
d->sketchKisView = qobject_cast<KisView*>(newView);
if(d->sketchKisView) {
d->sketchView->addActions(d->sketchKisView->actions());
// d->sketchKisView->setQtMainWindow(this);
connect(d->sketchKisView, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start()));
connect(d->sketchKisView->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)),
this, SLOT(resourceChangedSketch(int, const QVariant&)));
d->centerer->start();
}
emit sketchKisViewChanged();
}
}
void MainWindow::minimize()
{
setWindowState(windowState() ^ Qt::WindowMinimized);
}
void MainWindow::closeWindow()
{
if (d->desktopWindow) {
// This situation shouldn't occur, but protecting potentially dangerous call
d->desktopWindow->setNoCleanup(true);
}
//For some reason, close() does not work even if setAllowClose(true) was called just before this method.
//So instead just completely quit the application, since we are using a single window anyway.
DocumentManager::instance()->closeDocument();
qApp->processEvents();
QApplication::instance()->quit();
}
bool MainWindow::Private::queryClose()
{
if (desktopWindow) {
// This situation shouldn't occur, but protecting potentially dangerous call
desktopWindow->setNoCleanup(true);
}
if (DocumentManager::instance()->document() == 0)
return true;
// main doc + internally stored child documents
if (DocumentManager::instance()->document()->isModified()) {
QString name;
if (DocumentManager::instance()->document()->documentInfo()) {
name = DocumentManager::instance()->document()->documentInfo()->aboutInfo("title");
}
if (name.isEmpty())
name = DocumentManager::instance()->document()->url().fileName();
if (name.isEmpty())
name = i18n("Untitled");
int res = QMessageBox::warning(q,
i18nc("@title:window", "Krita"),
i18n("<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", name),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
switch (res) {
case QMessageBox::Yes : {
if (DocumentManager::instance()->isTemporaryFile()) {
if(!desktopViewProxy->fileSaveAs())
return false;
}
else if (!DocumentManager::instance()->save()) {
return false;
}
break;
}
case QMessageBox::No :
DocumentManager::instance()->document()->removeAutoSaveFiles();
DocumentManager::instance()->document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything.
break;
default : // case QMessageBox::Cancel :
return false;
}
}
return true;
}
void MainWindow::closeEvent(QCloseEvent* event)
{
if (centralWidget() == d->desktopWindow)
{
if (DocumentManager::instance()->document()->isLoading()) {
event->ignore();
return;
}
d->allowClose = d->queryClose();
}
if (d->allowClose)
{
if (d->desktopWindow)
{
d->desktopWindow->setNoCleanup(true);
d->desktopWindow->close();
}
event->accept();
}
else
{
event->ignore();
emit closeRequested();
}
}
MainWindow::~MainWindow()
{
delete d;
KisConfig cfg;
cfg.setNewCursorStyle(d->desktopCursorStyle);
}
#ifdef Q_OS_WIN
bool MainWindow::winEvent( MSG * message, long * result )
{
if (message && message->message == WM_SETTINGCHANGE && message->lParam)
{
if (wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *) message->lParam) == 0)
d->notifySlateModeChange();
else if (wcscmp(TEXT("SystemDockMode"), (TCHAR *) message->lParam) == 0)
d->notifyDockingModeChange();
*result = 0;
return true;
}
return false;
}
#endif
void MainWindow::Private::notifySlateModeChange()
{
#ifdef Q_OS_WIN
bool bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
if (slateMode != bSlateMode)
{
slateMode = bSlateMode;
emit q->slateModeChanged();
if (forceSketch || (slateMode && !forceDesktop))
{
if (!toSketch || (toSketch && toSketch->isEnabled()))
q->switchToSketch();
}
else
{
q->switchToDesktop();
}
- //qDebug() << "Slate mode is now" << slateMode;
+ //dbgKrita << "Slate mode is now" << slateMode;
}
#endif
}
void MainWindow::Private::notifyDockingModeChange()
{
#ifdef Q_OS_WIN
bool bDocked = (GetSystemMetrics(SM_SYSTEMDOCKED) != 0);
if (docked != bDocked)
{
docked = bDocked;
- //qDebug() << "Docking mode is now" << docked;
+ //dbgKrita << "Docking mode is now" << docked;
}
#endif
}
void MainWindow::cloneResources(KisCanvasResourceProvider *from, KisCanvasResourceProvider *to)
{
to->setBGColor(from->bgColor());
to->setFGColor(from->fgColor());
to->setHDRExposure(from->HDRExposure());
to->setHDRGamma(from->HDRGamma());
to->setCurrentCompositeOp(from->currentCompositeOp());
to->slotPatternActivated(from->currentPattern());
to->slotGradientActivated(from->currentGradient());
to->slotNodeActivated(from->currentNode());
to->setPaintOpPreset(from->currentPreset());
to->setOpacity(from->opacity());
to->setGlobalAlphaLock(from->globalAlphaLock());
}
bool MainWindow::forceFullScreen() {
return d->forceFullScreen;
}
void MainWindow::forceFullScreen(bool newValue)
{
d->forceFullScreen = newValue;
}
diff --git a/krita/image/CMakeLists.txt b/krita/image/CMakeLists.txt
index e9d4df61fa2..849a7eab203 100644
--- a/krita/image/CMakeLists.txt
+++ b/krita/image/CMakeLists.txt
@@ -1,392 +1,390 @@
add_subdirectory( tests )
########### next target ###############
kde_enable_exceptions()
# Chose a tiles backend
# 1 - image/tiles
# 3 - image/tiles3
set(USE_TILESYSTEM 3)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-tiles.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/../config-tiles.h) ### WRONG PLACE???
if(USE_TILESYSTEM EQUAL 3)
set(libkritatile_SRCS
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_tile.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_tile_data.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_tile_data_store.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_tile_data_pooler.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_tiled_data_manager.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_memento_manager.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_hline_iterator.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_vline_iterator.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/kis_random_accessor.cc
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_abstract_compression.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_lzf_compression.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_abstract_tile_compressor.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_legacy_tile_compressor.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_tile_compressor_2.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_chunk_allocator.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_memory_window.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_swapped_data_store.cpp
${CMAKE_SOURCE_DIR}/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
)
add_subdirectory( tiles3 )
endif()
option(HAVE_MEMORY_LEAK_TRACKER "Enable memory leak tracker (always disabled in release build)" OFF)
option(HAVE_BACKTRACE_SUPPORT "Enable recording of backtrace in memory leak tracker" OFF)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-memory-leak-tracker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-memory-leak-tracker.h) ### WRONG PLACE???
include_directories(
${CMAKE_SOURCE_DIR}/krita/image/metadata
3rdparty
)
if(FFTW3_FOUND)
include_directories(${FFTW3_INCLUDE_DIR})
endif()
if(HAVE_VC)
include_directories(${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
else()
set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
endif()
set(kritaimage_LIB_SRCS
${libkritatile_SRCS}
- kis_debug.cpp
kis_distance_information.cpp
kis_painter.cc
kis_progress_updater.cpp
brushengine/kis_paint_information.cc
brushengine/kis_paintop.cc
brushengine/kis_paintop_factory.cpp
brushengine/kis_paintop_preset.cpp
brushengine/kis_paintop_registry.cc
brushengine/kis_paintop_settings.cpp
brushengine/kis_locked_properties.cc
brushengine/kis_locked_properties_proxy.cpp
brushengine/kis_locked_properties_server.cpp
commands/kis_deselect_global_selection_command.cpp
commands/kis_image_change_layers_command.cpp
commands/kis_image_command.cpp
commands/kis_image_set_projection_color_space_command.cpp
commands/kis_image_layer_add_command.cpp
commands/kis_image_layer_move_command.cpp
commands/kis_image_layer_remove_command.cpp
commands/kis_image_layer_remove_command_impl.cpp
commands/kis_image_node_lower_command.cpp
commands/kis_image_node_raise_command.cpp
commands/kis_image_node_to_bottom_command.cpp
commands/kis_image_node_to_top_command.cpp
commands/kis_image_lock_command.cpp
commands/kis_layer_command.cpp
commands/kis_layer_props_command.cpp
commands/kis_node_command.cpp
commands/kis_node_compositeop_command.cpp
commands/kis_node_opacity_command.cpp
commands/kis_node_property_list_command.cpp
commands/kis_reselect_global_selection_command.cpp
commands/kis_set_global_selection_command.cpp
commands_new/kis_saved_commands.cpp
commands_new/kis_processing_command.cpp
commands_new/kis_image_resize_command.cpp
commands_new/kis_image_set_resolution_command.cpp
commands_new/kis_node_move_command2.cpp
commands_new/kis_set_layer_style_command.cpp
commands_new/kis_selection_move_command2.cpp
commands_new/kis_update_command.cpp
commands_new/kis_activate_selection_mask_command.cpp
processing/kis_do_nothing_processing_visitor.cpp
processing/kis_simple_processing_visitor.cpp
processing/kis_crop_processing_visitor.cpp
processing/kis_crop_selections_processing_visitor.cpp
processing/kis_transform_processing_visitor.cpp
processing/kis_mirror_processing_visitor.cpp
filter/kis_filter.cc
filter/kis_filter_configuration.cc
filter/kis_color_transformation_configuration.cc
filter/kis_filter_registry.cc
filter/kis_color_transformation_filter.cc
generator/kis_generator.cpp
generator/kis_generator_layer.cpp
generator/kis_generator_registry.cpp
floodfill/kis_fill_interval_map.cpp
floodfill/kis_scanline_fill.cpp
kis_adjustment_layer.cc
kis_selection_based_layer.cpp
kis_node_filter_interface.cpp
kis_base_accessor.cpp
kis_base_node.cpp
kis_base_processor.cpp
kis_basic_math_toolbox.cpp
kis_bookmarked_configuration_manager.cc
kis_clone_info.cpp
kis_clone_layer.cpp
kis_colorspace_convert_visitor.cpp
kis_config_widget.cpp
kis_convolution_kernel.cc
kis_convolution_painter.cc
kis_gaussian_kernel.cpp
kis_cubic_curve.cpp
kis_default_bounds.cpp
kis_default_bounds_base.cpp
kis_effect_mask.cc
kis_fast_math.cpp
kis_fill_painter.cc
kis_filter_mask.cpp
kis_filter_strategy.cc
kis_transform_mask.cpp
kis_transform_mask_params_interface.cpp
kis_recalculate_transform_mask_job.cpp
kis_recalculate_generator_layer_job.cpp
kis_transform_mask_params_factory_registry.cpp
kis_safe_transform.cpp
kis_gradient_painter.cc
kis_gradient_shape_strategy.cpp
kis_cached_gradient_shape_strategy.cpp
kis_polygonal_gradient_shape_strategy.cpp
kis_iterator_ng.cpp
kis_async_merger.cpp
kis_merge_walker.cc
kis_updater_context.cpp
kis_update_job_item.cpp
kis_stroke_strategy_undo_command_based.cpp
kis_simple_stroke_strategy.cpp
kis_stroke_job_strategy.cpp
kis_stroke_strategy.cpp
kis_stroke.cpp
kis_strokes_queue.cpp
kis_simple_update_queue.cpp
kis_update_scheduler.cpp
kis_queues_progress_updater.cpp
kis_composite_progress_proxy.cpp
kis_update_time_monitor.cpp
kis_group_layer.cc
kis_count_visitor.cpp
kis_histogram.cc
kis_image_interfaces.cpp
kis_node_graph_listener.cpp
kis_image.cc
kis_image_signal_router.cpp
kis_image_config.cpp
kis_crop_saved_extra_data.cpp
kis_signal_compressor.cpp
kis_signal_compressor_with_param.cpp
kis_thread_safe_signal_compressor.cpp
kis_acyclic_signal_connector.cpp
kis_layer.cc
kis_indirect_painting_support.cpp
kis_abstract_projection_plane.cpp
kis_layer_projection_plane.cpp
kis_mask_projection_plane.cpp
kis_projection_leaf.cpp
kis_mask.cc
kis_base_mask_generator.cpp
kis_rect_mask_generator.cpp
kis_circle_mask_generator.cpp
kis_gauss_circle_mask_generator.cpp
kis_gauss_rect_mask_generator.cpp
${__per_arch_circle_mask_generator_objs}
kis_curve_circle_mask_generator.cpp
kis_curve_rect_mask_generator.cpp
kis_math_toolbox.cpp
kis_memory_leak_tracker.cpp
kis_memory_statistics_server.cpp
kis_name_server.cpp
kis_node.cpp
kis_node_facade.cpp
kis_node_progress_proxy.cpp
kis_busy_progress_indicator.cpp
kis_node_visitor.cpp
kis_paint_device.cc
kis_fixed_paint_device.cpp
kis_paint_layer.cc
kis_perspective_grid.cpp
kis_perspective_math.cpp
kis_pixel_selection.cpp
kis_processing_information.cpp
kis_properties_configuration.cc
kis_random_accessor_ng.cpp
kis_random_generator.cc
kis_random_sub_accessor.cpp
kis_wrapped_random_accessor.cpp
kis_selection.cc
kis_selection_mask.cpp
kis_update_outline_job.cpp
kis_update_selection_job.cpp
kis_serializable_configuration.cc
kis_shared.cc
kis_transaction_data.cpp
kis_transform_worker.cc
kis_perspectivetransform_worker.cpp
bsplines/kis_bspline_1d.cpp
bsplines/kis_bspline_2d.cpp
bsplines/kis_nu_bspline_2d.cpp
kis_warptransform_worker.cc
kis_cage_transform_worker.cpp
kis_liquify_transform_worker.cpp
kis_green_coordinates_math.cpp
kis_algebra_2d.cpp
kis_dom_utils.cpp
kis_transparency_mask.cc
kis_undo_store.cpp
kis_undo_stores.cpp
kis_undo_adapter.cpp
kis_surrogate_undo_adapter.cpp
kis_legacy_undo_adapter.cpp
kis_post_execution_undo_adapter.cpp
kis_processing_visitor.cpp
kis_processing_applicator.cpp
krita_utils.cpp
kis_outline_generator.cpp
kis_layer_composition.cpp
kis_selection_filters.cpp
metadata/kis_meta_data_entry.cc
metadata/kis_meta_data_filter.cc
metadata/kis_meta_data_filter_p.cc
metadata/kis_meta_data_filter_registry.cc
metadata/kis_meta_data_filter_registry_model.cc
metadata/kis_meta_data_io_backend.cc
metadata/kis_meta_data_merge_strategy.cc
metadata/kis_meta_data_merge_strategy_p.cc
metadata/kis_meta_data_merge_strategy_registry.cc
metadata/kis_meta_data_parser.cc
metadata/kis_meta_data_schema.cc
metadata/kis_meta_data_schema_registry.cc
metadata/kis_meta_data_store.cc
metadata/kis_meta_data_type_info.cc
metadata/kis_meta_data_validator.cc
metadata/kis_meta_data_value.cc
recorder/kis_action_recorder.cc
recorder/kis_macro.cc
recorder/kis_macro_player.cc
recorder/kis_node_query_path.cc
recorder/kis_play_info.cc
recorder/kis_recorded_action.cc
recorder/kis_recorded_action_factory_registry.cc
recorder/kis_recorded_action_load_context.cpp
recorder/kis_recorded_action_save_context.cpp
recorder/kis_recorded_filter_action.cpp
recorder/kis_recorded_fill_paint_action.cpp
recorder/kis_recorded_node_action.cc
recorder/kis_recorded_paint_action.cpp
recorder/kis_recorded_path_paint_action.cpp
recorder/kis_recorded_shape_paint_action.cpp
kis_psd_layer_style.cpp
layerstyles/kis_layer_style_filter.cpp
layerstyles/kis_layer_style_filter_environment.cpp
layerstyles/kis_layer_style_filter_projection_plane.cpp
layerstyles/kis_layer_style_projection_plane.cpp
layerstyles/kis_ls_drop_shadow_filter.cpp
layerstyles/kis_ls_satin_filter.cpp
layerstyles/kis_ls_stroke_filter.cpp
layerstyles/kis_ls_bevel_emboss_filter.cpp
layerstyles/kis_ls_overlay_filter.cpp
layerstyles/kis_ls_utils.cpp
layerstyles/gimp_bump_map.cpp
)
set(einspline_SRCS
3rdparty/einspline/bspline_create.cpp
3rdparty/einspline/bspline_data.cpp
3rdparty/einspline/multi_bspline_create.cpp
3rdparty/einspline/nubasis.cpp
3rdparty/einspline/nubspline_create.cpp
3rdparty/einspline/nugrid.cpp
)
add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS})
# generate_export_header(kritaimage BASE_NAME kritaimage)
target_link_libraries(kritaimage kritaglobal kritapsd koodf pigmentcms kundo2 kowidgetutils Qt5::Concurrent)
target_link_libraries(kritaimage LINK_INTERFACE_LIBRARIES kritaglobal kritapsd koodf pigmentcms kundo2 kowidgetutils Qt5::Concurrent)
target_link_libraries(kritaimage ${Boost_SYSTEM_LIBRARY})
message("DEBUG_BOOST_LIBRARIES = " ${Boost_LIBRARIES})
message("DEBUG_BOOST_SYSTEM_FOUND = " ${Boost_SYSTEM_FOUND})
message("DEBUG_BOOST_SYSTEM_LIBRARY = " ${Boost_SYSTEM_LIBRARY})
if(OPENEXR_FOUND)
target_link_libraries(kritaimage ${OPENEXR_LIBRARIES})
endif()
if(FFTW3_FOUND)
target_link_libraries(kritaimage ${FFTW3_LIBRARIES})
endif()
if(HAVE_VC)
target_link_libraries(kritaimage ${Vc_LIBRARIES})
endif()
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.")
else ()
target_link_libraries(kritaimage ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif ()
set_target_properties(kritaimage PROPERTIES
VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS})
########### install schemas #############
install( FILES
metadata/schemas/dc.schema
metadata/schemas/exif.schema
metadata/schemas/tiff.schema
metadata/schemas/mkn.schema
metadata/schemas/xmp.schema
metadata/schemas/xmpmm.schema
metadata/schemas/xmprights.schema
DESTINATION ${DATA_INSTALL_DIR}/krita/metadata/schemas)
########### install files ###############
install( FILES
kis_base_node.h
kis_base_processor.h
kis_config_widget.h
kis_convolution_kernel.h
kis_convolution_painter.h
kis_convolution_worker.h
kis_cubic_curve.h
- kis_debug.h
kis_default_bounds.h
kis_distance_information.h
filter/kis_filter.h
filter/kis_filter_registry.h
kis_filter_strategy.h
kis_global.h
kis_image.h
kis_mask.h
kis_node.h
kis_node_facade.h
kis_node_graph_listener.h
kis_painter.h
kis_paint_device.h
kis_properties_configuration.h
kis_processing_information.h
kis_transform_worker.h
kis_perspectivetransform_worker.h
kis_warptransform_worker.h
kis_serializable_configuration.h
kis_selection.h
kis_shared.h
kis_shared_ptr.h
kis_transaction.h
kis_types.h
kritaimage_export.h
filter/kis_filter_configuration.h
generator/kis_generator.h
generator/kis_generator_registry.h
brushengine/kis_locked_properties.h
brushengine/kis_locked_properties_proxy.h
brushengine/kis_locked_properties_server.h
DESTINATION ${INCLUDE_INSTALL_DIR}/krita)
diff --git a/krita/image/brushengine/kis_paint_information.cc b/krita/image/brushengine/kis_paint_information.cc
index 57e55669cd5..36aebc01372 100644
--- a/krita/image/brushengine/kis_paint_information.cc
+++ b/krita/image/brushengine/kis_paint_information.cc
@@ -1,499 +1,499 @@
/*
* Copyright (c) 2007,2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paint_information.h"
#include <QDomElement>
#include <QScopedPointer>
#include "kis_paintop.h"
#include "kis_distance_information.h"
#include "kis_algebra_2d.h"
struct KisPaintInformation::Private {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Private(const QPointF & pos_,
qreal pressure_,
qreal xTilt_, qreal yTilt_,
qreal rotation_,
qreal tangentialPressure_,
qreal perspective_,
qreal time_,
qreal speed_,
bool isHoveringMode_)
:
pos(pos_),
pressure(pressure_),
xTilt(xTilt_),
yTilt(yTilt_),
rotation(rotation_),
tangentialPressure(tangentialPressure_),
perspective(perspective_),
time(time_),
speed(speed_),
isHoveringMode(isHoveringMode_),
currentDistanceInfo(0)
{
}
~Private() {
KIS_ASSERT_RECOVER_NOOP(!currentDistanceInfo);
}
Private(const Private &rhs) {
copy(rhs);
}
Private& operator=(const Private &rhs) {
copy(rhs);
return *this;
}
void copy(const Private &rhs) {
pos = rhs.pos;
pressure = rhs.pressure;
xTilt = rhs.xTilt;
yTilt = rhs.yTilt;
rotation = rhs.rotation;
tangentialPressure = rhs.tangentialPressure;
perspective = rhs.perspective;
time = rhs.time;
speed = rhs.speed;
isHoveringMode = rhs.isHoveringMode;
currentDistanceInfo = rhs.currentDistanceInfo;
canvasRotation = rhs.canvasRotation;
canvasMirroredH = rhs.canvasMirroredH;
if (rhs.drawingAngleOverride) {
drawingAngleOverride.reset(new qreal(*rhs.drawingAngleOverride));
}
}
QPointF pos;
qreal pressure;
qreal xTilt;
qreal yTilt;
qreal rotation;
qreal tangentialPressure;
qreal perspective;
qreal time;
qreal speed;
bool isHoveringMode;
int canvasRotation;
bool canvasMirroredH;
QScopedPointer<qreal> drawingAngleOverride;
KisDistanceInformation *currentDistanceInfo;
void registerDistanceInfo(KisDistanceInformation *di) {
currentDistanceInfo = di;
}
void unregisterDistanceInfo() {
currentDistanceInfo = 0;
}
};
KisPaintInformation::DistanceInformationRegistrar::
DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo)
: p(_p)
{
p->d->registerDistanceInfo(distanceInfo);
}
KisPaintInformation::DistanceInformationRegistrar::
~DistanceInformationRegistrar()
{
p->d->unregisterDistanceInfo();
}
KisPaintInformation::KisPaintInformation(const QPointF & pos,
qreal pressure,
qreal xTilt, qreal yTilt,
qreal rotation,
qreal tangentialPressure,
qreal perspective,
qreal time,
qreal speed)
: d(new Private(pos,
pressure,
xTilt, yTilt,
rotation,
tangentialPressure,
perspective,
time,
speed,
false))
{
}
KisPaintInformation::KisPaintInformation(const QPointF & pos,
qreal pressure,
qreal xTilt,
qreal yTilt,
qreal rotation)
: d(new Private(pos,
pressure,
xTilt, yTilt,
rotation,
0.0,
1.0,
0.0,
0.0,
false))
{
}
KisPaintInformation::KisPaintInformation(const QPointF &pos,
qreal pressure)
: d(new Private(pos,
pressure,
0.0, 0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
false))
{
}
KisPaintInformation::KisPaintInformation(const KisPaintInformation& rhs)
: d(new Private(*rhs.d))
{
}
void KisPaintInformation::operator=(const KisPaintInformation & rhs)
{
*d = *rhs.d;
}
KisPaintInformation::~KisPaintInformation()
{
delete d;
}
bool KisPaintInformation::isHoveringMode() const
{
return d->isHoveringMode;
}
KisPaintInformation
KisPaintInformation::createHoveringModeInfo(const QPointF &pos,
qreal pressure,
qreal xTilt, qreal yTilt,
qreal rotation,
qreal tangentialPressure,
qreal perspective,
qreal speed,
int canvasrotation,
bool canvasMirroredH)
{
KisPaintInformation info(pos,
pressure,
xTilt, yTilt,
rotation,
tangentialPressure,
perspective, 0, speed);
info.d->isHoveringMode = true;
info.d->canvasRotation = canvasrotation;
info.d->canvasMirroredH = canvasMirroredH;
return info;
}
int KisPaintInformation::canvasRotation() const
{
return d->canvasRotation;
}
void KisPaintInformation::setCanvasRotation(int rotation)
{
if (rotation < 0) {
d->canvasRotation= 360- abs(rotation % 360);
} else {
d->canvasRotation= rotation % 360;
}
}
bool KisPaintInformation::canvasMirroredH() const
{
return d->canvasMirroredH;
}
void KisPaintInformation::setCanvasHorizontalMirrorState(bool mir)
{
d->canvasMirroredH = mir;
}
void KisPaintInformation::toXML(QDomDocument&, QDomElement& e) const
{
// hovering mode infos are not supposed to be saved
KIS_ASSERT_RECOVER_NOOP(!d->isHoveringMode);
e.setAttribute("pointX", QString::number(pos().x(), 'g', 15));
e.setAttribute("pointY", QString::number(pos().y(), 'g', 15));
e.setAttribute("pressure", QString::number(pressure(), 'g', 15));
e.setAttribute("xTilt", QString::number(xTilt(), 'g', 15));
e.setAttribute("yTilt", QString::number(yTilt(), 'g', 15));
e.setAttribute("rotation", QString::number(rotation(), 'g', 15));
e.setAttribute("tangentialPressure", QString::number(tangentialPressure(), 'g', 15));
e.setAttribute("perspective", QString::number(perspective(), 'g', 15));
e.setAttribute("time", d->time);
e.setAttribute("speed", d->speed);
}
KisPaintInformation KisPaintInformation::fromXML(const QDomElement& e)
{
qreal pointX = qreal(e.attribute("pointX", "0.0").toDouble());
qreal pointY = qreal(e.attribute("pointY", "0.0").toDouble());
qreal pressure = qreal(e.attribute("pressure", "0.0").toDouble());
qreal rotation = qreal(e.attribute("rotation", "0.0").toDouble());
qreal tangentialPressure = qreal(e.attribute("tangentialPressure", "0.0").toDouble());
qreal perspective = qreal(e.attribute("perspective", "0.0").toDouble());
qreal xTilt = qreal(e.attribute("xTilt", "0.0").toDouble());
qreal yTilt = qreal(e.attribute("yTilt", "0.0").toDouble());
qreal time = e.attribute("time", "0").toDouble();
qreal speed = e.attribute("speed", "0").toDouble();
return KisPaintInformation(QPointF(pointX, pointY), pressure, xTilt, yTilt,
rotation, tangentialPressure, perspective, time, speed);
}
const QPointF& KisPaintInformation::pos() const
{
return d->pos;
}
void KisPaintInformation::setPos(const QPointF& p)
{
d->pos = p;
}
qreal KisPaintInformation::pressure() const
{
return d->pressure;
}
void KisPaintInformation::setPressure(qreal p)
{
d->pressure = p;
}
qreal KisPaintInformation::xTilt() const
{
return d->xTilt;
}
qreal KisPaintInformation::yTilt() const
{
return d->yTilt;
}
void KisPaintInformation::overrideDrawingAngle(qreal angle)
{
d->drawingAngleOverride.reset(new qreal(angle));
}
qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distance) const
{
if (d->drawingAngleOverride) return *d->drawingAngleOverride;
QVector2D diff(pos() - distance.lastPosition());
return atan2(diff.y(), diff.x());
}
KisPaintInformation::DistanceInformationRegistrar
KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance)
{
return DistanceInformationRegistrar(this, distance);
}
qreal KisPaintInformation::drawingAngle() const
{
if (d->drawingAngleOverride) return *d->drawingAngleOverride;
if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) {
- qWarning() << "KisPaintInformation::drawingAngle()" << "Cannot access Distance Info last dab data";
+ warnKrita << "KisPaintInformation::drawingAngle()" << "Cannot access Distance Info last dab data";
return 0.0;
}
QVector2D diff(pos() - d->currentDistanceInfo->lastPosition());
return atan2(diff.y(), diff.x());
}
QPointF KisPaintInformation::drawingDirectionVector() const
{
if (d->drawingAngleOverride) {
qreal angle = *d->drawingAngleOverride;
return QPointF(cos(angle), sin(angle));
}
if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) {
- qWarning() << "KisPaintInformation::drawingDirectionVector()" << "Cannot access Distance Info last dab data";
+ warnKrita << "KisPaintInformation::drawingDirectionVector()" << "Cannot access Distance Info last dab data";
return QPointF(1.0, 0.0);
}
QPointF diff(pos() - d->currentDistanceInfo->lastPosition());
return KisAlgebra2D::normalize(diff);
}
qreal KisPaintInformation::drawingDistance() const
{
if (!d->currentDistanceInfo || !d->currentDistanceInfo->hasLastDabInformation()) {
- qWarning() << "KisPaintInformation::drawingDistance()" << "Cannot access Distance Info last dab data";
+ warnKrita << "KisPaintInformation::drawingDistance()" << "Cannot access Distance Info last dab data";
return 1.0;
}
QVector2D diff(pos() - d->currentDistanceInfo->lastPosition());
return diff.length();
}
qreal KisPaintInformation::drawingSpeed() const
{
return d->speed;
}
qreal KisPaintInformation::rotation() const
{
return d->rotation;
}
qreal KisPaintInformation::tangentialPressure() const
{
return d->tangentialPressure;
}
qreal KisPaintInformation::perspective() const
{
return d->perspective;
}
qreal KisPaintInformation::currentTime() const
{
return d->time;
}
QDebug operator<<(QDebug dbg, const KisPaintInformation &info)
{
#ifdef NDEBUG
Q_UNUSED(info);
#else
dbg.nospace() << "Position: " << info.pos();
dbg.nospace() << ", Pressure: " << info.pressure();
dbg.nospace() << ", X Tilt: " << info.xTilt();
dbg.nospace() << ", Y Tilt: " << info.yTilt();
dbg.nospace() << ", Rotation: " << info.rotation();
dbg.nospace() << ", Tangential Pressure: " << info.tangentialPressure();
dbg.nospace() << ", Perspective: " << info.perspective();
dbg.nospace() << ", Drawing Angle: " << info.drawingAngle();
dbg.nospace() << ", Drawing Speed: " << info.drawingSpeed();
dbg.nospace() << ", Drawing Distance: " << info.drawingDistance();
dbg.nospace() << ", Time: " << info.currentTime();
#endif
return dbg.space();
}
KisPaintInformation KisPaintInformation::mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi)
{
QPointF pt = (1 - t) * mixedPi.pos() + t * basePi.pos();
KisPaintInformation result(pt,
basePi.pressure(),
basePi.xTilt(),
basePi.yTilt(),
basePi.rotation(),
basePi.tangentialPressure(),
basePi.perspective(),
basePi.currentTime(),
basePi.drawingSpeed());
return result;
}
KisPaintInformation KisPaintInformation::mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
{
QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos();
return mix(pt, t, pi1, pi2);
}
KisPaintInformation KisPaintInformation::mix(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
{
qreal pressure = (1 - t) * pi1.pressure() + t * pi2.pressure();
qreal xTilt = (1 - t) * pi1.xTilt() + t * pi2.xTilt();
qreal yTilt = (1 - t) * pi1.yTilt() + t * pi2.yTilt();
qreal rotation = pi1.rotation();
if (pi1.rotation() != pi2.rotation()) {
qreal a1 = kisDegreesToRadians(pi1.rotation());
qreal a2 = kisDegreesToRadians(pi2.rotation());
qreal distance = shortestAngularDistance(a2, a1);
rotation = kisRadiansToDegrees(incrementInDirection(a1, t * distance, a2));
}
qreal tangentialPressure = (1 - t) * pi1.tangentialPressure() + t * pi2.tangentialPressure();
qreal perspective = (1 - t) * pi1.perspective() + t * pi2.perspective();
qreal time = (1 - t) * pi1.currentTime() + t * pi2.currentTime();
qreal speed = (1 - t) * pi1.drawingSpeed() + t * pi2.drawingSpeed();
KisPaintInformation result(p, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed);
KIS_ASSERT_RECOVER_NOOP(pi1.isHoveringMode() == pi2.isHoveringMode());
result.d->isHoveringMode = pi1.isHoveringMode();
result.d->canvasRotation = pi2.canvasRotation();
result.d->canvasMirroredH = pi2.canvasMirroredH();
return result;
}
qreal KisPaintInformation::tiltDirection(const KisPaintInformation& info, bool normalize)
{
qreal xTilt = info.xTilt();
qreal yTilt = info.yTilt();
// radians -PI, PI
qreal tiltDirection = atan2(-xTilt, yTilt);
// if normalize is true map to 0.0..1.0
return normalize ? (tiltDirection / (2 * M_PI) + 0.5) : tiltDirection;
}
qreal KisPaintInformation::tiltElevation(const KisPaintInformation& info, qreal maxTiltX, qreal maxTiltY, bool normalize)
{
qreal xTilt = qBound(qreal(-1.0), info.xTilt() / maxTiltX , qreal(1.0));
qreal yTilt = qBound(qreal(-1.0), info.yTilt() / maxTiltY , qreal(1.0));
qreal e;
if (fabs(xTilt) > fabs(yTilt)) {
e = sqrt(qreal(1.0) + yTilt * yTilt);
} else {
e = sqrt(qreal(1.0) + xTilt * xTilt);
}
qreal cosAlpha = sqrt(xTilt * xTilt + yTilt * yTilt) / e;
qreal tiltElevation = acos(cosAlpha); // in radians in [0, 0.5 * PI]
// mapping to 0.0..1.0 if normalize is true
return normalize ? (tiltElevation / (M_PI * qreal(0.5))) : tiltElevation;
}
diff --git a/krita/image/brushengine/kis_paint_information.h b/krita/image/brushengine/kis_paint_information.h
index 2544c75dcb3..4b63d33fa7d 100644
--- a/krita/image/brushengine/kis_paint_information.h
+++ b/krita/image/brushengine/kis_paint_information.h
@@ -1,252 +1,252 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_PAINT_INFORMATION_
#define _KIS_PAINT_INFORMATION_
-#include <QDebug>
+#include <kis_debug.h>
#include <QTime>
#include "kis_global.h"
#include "kis_vec.h"
#include "kritaimage_export.h"
#include "kis_distance_information.h"
class QDomDocument;
class QDomElement;
class KisDistanceInformation;
/**
* KisPaintInformation contains information about the input event that
* causes the brush action to happen to the brush engine's paint
* methods.
*
* XXX: we directly pass the KoPointerEvent x and y tilt to
* KisPaintInformation, and their range is -60 to +60!
*
* @param pos: the position of the paint event in subpixel accuracy
* @param pressure: the pressure of the stylus
* @param xTilt: the angle between the device (a pen, for example) and
* the perpendicular in the direction of the x axis. Positive values
* are towards the bottom of the tablet. The angle is within the range
* 0 to 1
* @param yTilt: the angle between the device (a pen, for example) and
* the perpendicular in the direction of the y axis. Positive values
* are towards the bottom of the tablet. The angle is within the range
* 0 to .
* @param movement: current position minus the last position of the call to paintAt
* @param rotation
* @param tangentialPressure
* @param perspective
**/
class KRITAIMAGE_EXPORT KisPaintInformation
{
public:
/**
* Note, that this class is relied on the compiler optimization
* of the return value. So if it doesn't work for some reason,
* please implement a proper copy c-tor
*/
class KRITAIMAGE_EXPORT DistanceInformationRegistrar
{
public:
DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo);
~DistanceInformationRegistrar();
private:
KisPaintInformation *p;
};
public:
/**
* Create a new KisPaintInformation object.
*/
KisPaintInformation(const QPointF & pos,
qreal pressure,
qreal xTilt,
qreal yTilt,
qreal rotation,
qreal tangentialPressure,
qreal perspective,
qreal time,
qreal speed);
KisPaintInformation(const QPointF & pos,
qreal pressure,
qreal xTilt,
qreal yTilt,
qreal rotation);
KisPaintInformation(const QPointF & pos = QPointF(),
qreal pressure = PRESSURE_DEFAULT);
KisPaintInformation(const KisPaintInformation& rhs);
void operator=(const KisPaintInformation& rhs);
~KisPaintInformation();
template <class PaintOp>
void paintAt(PaintOp &op, KisDistanceInformation *distanceInfo) {
KisSpacingInformation spacingInfo;
{
DistanceInformationRegistrar r = registerDistanceInformation(distanceInfo);
spacingInfo = op.paintAt(*this);
}
distanceInfo->registerPaintedDab(*this, spacingInfo);
}
const QPointF& pos() const;
void setPos(const QPointF& p);
/// The pressure of the value (from 0.0 to 1.0)
qreal pressure() const;
/// Set the pressure
void setPressure(qreal p);
/// The tilt of the pen on the horizontal axis (from 0.0 to 1.0)
qreal xTilt() const;
/// The tilt of the pen on the vertical axis (from 0.0 to 1.0)
qreal yTilt() const;
/// XXX !!! :-| Please add dox!
void overrideDrawingAngle(qreal angle);
/// XXX !!! :-| Please add dox!
qreal drawingAngleSafe(const KisDistanceInformation &distance) const;
/// XXX !!! :-| Please add dox!
DistanceInformationRegistrar registerDistanceInformation(KisDistanceInformation *distance);
/**
* Current brush direction computed from the cursor movement
*
* WARNING: this method is available *only* inside paintAt() call,
* that is when the distance information is registered.
*/
qreal drawingAngle() const;
/**
* Current brush direction vector computed from the cursor movement
*
* WARNING: this method is available *only* inside paintAt() call,
* that is when the distance information is registered.
*/
QPointF drawingDirectionVector() const;
/**
* Current brush speed computed from the cursor movement
*
* WARNING: this method is available *only* inside paintAt() call,
* that is when the distance information is registered.
*/
qreal drawingSpeed() const;
/**
* Current distance from the previous dab
*
* WARNING: this method is available *only* inside paintAt() call,
* that is when the distance information is registered.
*/
qreal drawingDistance() const;
/// rotation as given by the tablet event
qreal rotation() const;
/// tangential pressure (i.e., rate for an airbrush device)
qreal tangentialPressure() const;
/// reciprocal of distance on the perspective grid
qreal perspective() const;
/// Number of ms since the beginning of the stroke
qreal currentTime() const;
/**
* The paint information may be generated not only during real
* stroke when the actual painting is happening, but also when the
* cursor is hovering the canvas. In this mode some of the sensors
* work a bit differently. The most outstanding example is Fuzzy
* sensor, which returns unit value in this mode, otherwise it is
* too irritating for a user.
*
* This value is true only for paint information objects created with
* createHoveringModeInfo() constructor.
*
* \see createHoveringModeInfo()
*/
bool isHoveringMode() const;
/**
* Create a fake info object with isHoveringMode() property set to
* true.
*
* \see isHoveringMode()
*/
static KisPaintInformation createHoveringModeInfo(const QPointF &pos,
qreal pressure = PRESSURE_DEFAULT,
qreal xTilt = 0.0, qreal yTilt = 0.0,
qreal rotation = 0.0,
qreal tangentialPressure = 0.0,
qreal perspective = 1.0,
qreal speed = 0.0,
int canvasrotation = 0,
bool canvasMirroredH = false);
/**
*Returns the canvas rotation if that has been given to the kispaintinformation.
*/
int canvasRotation() const;
/**
*set the canvas rotation.
*/
void setCanvasRotation(int rotation);
/*
*Whether the canvas is mirrored for the paint-operation.
*/
bool canvasMirroredH() const;
/*
*Set whether the canvas is mirrored for the paint-operation.
*/
void setCanvasHorizontalMirrorState(bool mir);
void toXML(QDomDocument&, QDomElement&) const;
static KisPaintInformation fromXML(const QDomElement&);
/// (1-t) * p1 + t * p2
static KisPaintInformation mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi);
static KisPaintInformation mix(const QPointF& p, qreal t, const KisPaintInformation& p1, const KisPaintInformation& p2);
static KisPaintInformation mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2);
static qreal tiltDirection(const KisPaintInformation& info, bool normalize = true);
static qreal tiltElevation(const KisPaintInformation& info, qreal maxTiltX = 60.0, qreal maxTiltY = 60.0, bool normalize = true);
private:
struct Private;
Private* const d;
};
KRITAIMAGE_EXPORT QDebug operator<<(QDebug debug, const KisPaintInformation& info);
#endif
diff --git a/krita/image/brushengine/kis_paintop_config_widget.h b/krita/image/brushengine/kis_paintop_config_widget.h
index e59a5bdc690..822c7b31f98 100644
--- a/krita/image/brushengine/kis_paintop_config_widget.h
+++ b/krita/image/brushengine/kis_paintop_config_widget.h
@@ -1,93 +1,93 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_PAINTOP_CONFIG_WIDGET_H_
#define KIS_PAINTOP_CONFIG_WIDGET_H_
#include "kritaimage_export.h"
#include "kis_config_widget.h"
#include "kis_image.h"
-#include <kdebug.h>
+#include <kis_debug.h>
/**
* Base class for widgets that are used to edit and display paintop settings.
*/
class KRITAIMAGE_EXPORT KisPaintOpConfigWidget : public KisConfigWidget
{
public:
KisPaintOpConfigWidget(QWidget * parent = 0, Qt::WFlags f = 0)
: KisConfigWidget(parent, f, 10) {
}
virtual ~KisPaintOpConfigWidget() {
}
/**
* Write the settings in this widget to the given properties
* configuration, which is cleared first.
*/
virtual void writeConfiguration(KisPropertiesConfiguration *config) const = 0;
virtual void setImage(KisImageWSP image) {
m_image = image;
}
virtual void setNode(KisNodeWSP node) {
m_node = node;
}
/**
* @see KisPaintOpSettings::changePaintOpSize(qreal x, qreal y)
*/
virtual void changePaintOpSize(qreal x, qreal y) {
Q_UNUSED(x);
Q_UNUSED(y);
}
/**
* @see KisPaintOpSettings::paintOpSize()
*/
virtual QSizeF paintOpSize() const {
return QSizeF(1.0, 1.0);
};
/**
* This is true for all of the paintop widget except for the Custom brush tab in the Brush tip dialog
*/
virtual bool presetIsValid() {
return true;
}
/**
* Some paintops are more complicated and require full canvas with layers, projections and KisImage etc.
* Example is duplicate paintop. In this case simple canvas like scratchbox does not work.
* Every paintop supports the scratchbox by default, override and return false if paintop does not.
*/
virtual bool supportScratchBox() {
return true;
}
protected:
KisImageWSP m_image;
KisNodeWSP m_node;
};
#endif
diff --git a/krita/image/brushengine/kis_paintop_preset.cpp b/krita/image/brushengine/kis_paintop_preset.cpp
index 120f31f3f2e..c373cc0a97b 100644
--- a/krita/image/brushengine/kis_paintop_preset.cpp
+++ b/krita/image/brushengine/kis_paintop_preset.cpp
@@ -1,346 +1,346 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
* Copyright (C) Sven Langkamp <sven.langkamp@gmail.com>, (C) 2009
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_preset.h"
#include <QFile>
#include <QSize>
#include <QImage>
#include <QImageWriter>
#include <QImageReader>
#include <QDomDocument>
#include <QBuffer>
#include <KoInputDevice.h>
#include "kis_types.h"
#include "kis_paintop_settings.h"
#include "kis_paintop_registry.h"
#include "kis_painter.h"
#include "kis_paint_information.h"
#include "kis_paint_device.h"
#include "kis_image.h"
#include <KoStore.h>
struct Q_DECL_HIDDEN KisPaintOpPreset::Private {
Private()
: settings(0),
dirtyPreset(false)
{
}
KisPaintOpSettingsSP settings;
bool dirtyPreset;
};
KisPaintOpPreset::KisPaintOpPreset()
: KoResource(QString())
, m_d(new Private)
{
}
KisPaintOpPreset::KisPaintOpPreset(const QString & fileName)
: KoResource(fileName)
, m_d(new Private)
{
}
KisPaintOpPreset::~KisPaintOpPreset()
{
delete m_d;
}
KisPaintOpPresetSP KisPaintOpPreset::clone() const
{
KisPaintOpPresetSP preset = new KisPaintOpPreset();
if (settings()) {
preset->setSettings(settings()->clone());
}
preset->setPresetDirty(isPresetDirty());
// only valid if we could clone the settings
preset->setValid(settings());
preset->setPaintOp(paintOp());
preset->setName(name());
preset->setImage(image());
preset->settings()->setPreset(KisPaintOpPresetWSP(preset));
Q_ASSERT(preset->valid());
return preset;
}
void KisPaintOpPreset::setPresetDirty(bool value)
{
m_d->dirtyPreset = value;
}
bool KisPaintOpPreset::isPresetDirty() const
{
return m_d->dirtyPreset;
}
void KisPaintOpPreset::setPaintOp(const KoID & paintOp)
{
Q_ASSERT(m_d->settings);
m_d->settings->setProperty("paintop", paintOp.id());
}
KoID KisPaintOpPreset::paintOp() const
{
Q_ASSERT(m_d->settings);
return KoID(m_d->settings->getString("paintop"), name());
}
void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings)
{
Q_ASSERT(settings);
Q_ASSERT(!settings->getString("paintop", "").isEmpty());
DirtyStateSaver dirtyStateSaver(this);
if (settings) {
m_d->settings = settings->clone();
m_d->settings->setPreset(KisPaintOpPresetWSP(this));
} else {
m_d->settings = 0;
m_d->settings->setPreset(0);
}
setValid(m_d->settings);
}
KisPaintOpSettingsSP KisPaintOpPreset::settings() const
{
Q_ASSERT(m_d->settings);
Q_ASSERT(!m_d->settings->getString("paintop", "").isEmpty());
return m_d->settings;
}
bool KisPaintOpPreset::load()
{
dbgImage << "Load preset " << filename();
setValid(false);
if (filename().isEmpty()) {
return false;
}
QIODevice *dev = 0;
QByteArray ba;
if (filename().startsWith("bundle://")) {
- qDebug() << "bundle";
+ dbgKrita << "bundle";
QString bn = filename().mid(9);
int pos = bn.lastIndexOf(":");
QString fn = bn.right(bn.size() - pos - 1);
bn = bn.left(pos);
QScopedPointer<KoStore> resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
if (!resourceStore || resourceStore->bad()) {
- qWarning() << "Could not open store on bundle" << bn;
+ warnKrita << "Could not open store on bundle" << bn;
return false;
}
if (resourceStore->isOpen()) resourceStore->close();
if (!resourceStore->open(fn)) {
- qWarning() << "Could not open preset" << fn << "in bundle" << bn;
+ warnKrita << "Could not open preset" << fn << "in bundle" << bn;
return false;
}
ba = resourceStore->device()->readAll();
dev = new QBuffer(&ba);
resourceStore->close();
}
else {
dev = new QFile(filename());
if (dev->size() == 0)
{
delete dev;
return false;
}
if (!dev->open(QIODevice::ReadOnly)) {
warnKrita << "Can't open file " << filename();
delete dev;
return false;
}
}
bool res = loadFromDevice(dev);
delete dev;
setValid(res);
setPresetDirty(false);
return res;
}
bool KisPaintOpPreset::loadFromDevice(QIODevice *dev)
{
QImageReader reader(dev, "PNG");
QString version = reader.text("version");
QString preset = reader.text("preset");
dbgImage << version;
if (version != "2.2") {
return false;
}
QImage img;
if (!reader.read(&img)) {
dbgImage << "Fail to decode PNG";
return false;
}
//Workaround for broken presets
//Presets was saved with nested cdata section
preset.replace("<curve><![CDATA[", "<curve>");
preset.replace("]]></curve>", "</curve>");
QDomDocument doc;
if (!doc.setContent(preset)) {
return false;
}
fromXML(doc.documentElement());
if (!m_d->settings) {
return false;
}
setValid(true);
setImage(img);
return true;
}
bool KisPaintOpPreset::save()
{
if (filename().isEmpty())
return false;
QString paintopid = m_d->settings->getString("paintop", "");
if (paintopid.isEmpty())
return false;
QFile f(filename());
f.open(QFile::WriteOnly);
return saveToDevice(&f);
}
void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const
{
QString paintopid = m_d->settings->getString("paintop", "");
elt.setAttribute("paintopid", paintopid);
elt.setAttribute("name", name());
// sanitize the settings
bool hasTexture = m_d->settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
foreach(const QString & key, m_d->settings->getProperties().keys()) {
if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
m_d->settings->removeProperty(key);
}
}
}
m_d->settings->toXML(doc, elt);
}
void KisPaintOpPreset::fromXML(const QDomElement& presetElt)
{
setName(presetElt.attribute("name"));
QString paintopid = presetElt.attribute("paintopid");
if (paintopid.isEmpty()) {
dbgImage << "No paintopid attribute";
setValid(false);
return;
}
if (KisPaintOpRegistry::instance()->get(paintopid) == 0) {
dbgImage << "No paintop " << paintopid;
setValid(false);
return;
}
KoID id(paintopid, "");
KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(id);
if (!settings) {
setValid(false);
- qWarning() << "Could not load settings for preset" << paintopid;
+ warnKrita << "Could not load settings for preset" << paintopid;
return;
}
settings->fromXML(presetElt);
// sanitize the settings
bool hasTexture = settings->getBool("Texture/Pattern/Enabled");
if (!hasTexture) {
foreach(const QString & key, settings->getProperties().keys()) {
if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
settings->removeProperty(key);
}
}
}
setSettings(settings);
}
bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const
{
QImageWriter writer(dev, "PNG");
QDomDocument doc;
QDomElement root = doc.createElement("Preset");
toXML(doc, root);
doc.appendChild(root);
writer.setText("version", "2.2");
writer.setText("preset", doc.toString());
QImage img;
if (image().isNull()) {
img = QImage(1, 1, QImage::Format_RGB32);
} else {
img = image();
}
m_d->dirtyPreset = false;
KoResource::saveToDevice(dev);
return writer.write(img);
}
diff --git a/krita/image/brushengine/kis_paintop_registry.cc b/krita/image/brushengine/kis_paintop_registry.cc
index ab8a83f472d..d7ce78cc345 100644
--- a/krita/image/brushengine/kis_paintop_registry.cc
+++ b/krita/image/brushengine/kis_paintop_registry.cc
@@ -1,167 +1,167 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_registry.h"
#include <kglobal.h>
#include <klocale.h>
#include <KoGenericRegistry.h>
#include <KoPluginLoader.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOp.h>
#include <KoID.h>
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_paintop.h"
#include "kis_painter.h"
#include "kis_debug.h"
#include "kis_layer.h"
#include "kis_image.h"
#include "kis_paintop_config_widget.h"
KisPaintOpRegistry::KisPaintOpRegistry()
{
}
KisPaintOpRegistry::~KisPaintOpRegistry()
{
foreach(const QString & id, keys()) {
delete get(id);
}
dbgRegistry << "Deleting KisPaintOpRegistry";
}
KisPaintOpRegistry* KisPaintOpRegistry::instance()
{
K_GLOBAL_STATIC(KisPaintOpRegistry, s_instance);
if (!s_instance.exists()) {
KoPluginLoader::instance()->load("Krita/Paintop", "(Type == 'Service') and ([X-Krita-Version] == 28)");
QStringList toBeRemoved;
foreach(const QString & id, s_instance->keys()) {
KisPaintOpFactory *factory = s_instance->get(id);
if (!factory->settings()) {
toBeRemoved << id;
} else {
factory->processAfterLoading();
}
}
foreach(const QString & id, toBeRemoved) {
s_instance->remove(id);
}
}
return s_instance;
}
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
void KisPaintOpRegistry::preinitializePaintOpIfNeeded(const KisPaintOpPresetSP preset)
{
if (!preset) return;
KisPaintOpFactory *f = value(preset->paintOp().id());
f->preinitializePaintOpIfNeeded(preset->settings());
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
KisPaintOp * KisPaintOpRegistry::paintOp(const QString & id, const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) const
{
if (painter == 0) {
warnKrita << " KisPaintOpRegistry::paintOp painter is null";
return 0;
}
Q_ASSERT(settings);
KisPaintOpFactory* f = value(id);
if (f) {
KisPaintOp * op = f->createOp(settings, painter, node, image);
if (op) {
return op;
}
}
- qWarning() << "Could not create paintop for factory" << id << "with settings" << settings;
+ warnKrita << "Could not create paintop for factory" << id << "with settings" << settings;
return 0;
}
KisPaintOp * KisPaintOpRegistry::paintOp(const KisPaintOpPresetSP preset, KisPainter * painter, KisNodeSP node, KisImageSP image) const
{
Q_ASSERT(preset);
Q_ASSERT(painter);
if (!preset) return 0;
return paintOp(preset->paintOp().id(), preset->settings(), painter, node, image);
}
KisPaintOpSettingsSP KisPaintOpRegistry::settings(const KoID& id) const
{
KisPaintOpFactory *f = value(id.id());
Q_ASSERT(f);
if (f) {
KisPaintOpSettingsSP settings = f->settings();
settings->setProperty("paintop", id.id());
return settings;
}
return 0;
}
KisPaintOpPresetSP KisPaintOpRegistry::defaultPreset(const KoID& id) const
{
KisPaintOpPresetSP preset = new KisPaintOpPreset();
preset->setName(i18n("default"));
KisPaintOpSettingsSP s = settings(id);
if (s.isNull()) {
return 0;
}
preset->setSettings(s);
preset->setPaintOp(id);
Q_ASSERT(!preset->paintOp().id().isEmpty());
preset->setValid(true);
return preset;
}
QString KisPaintOpRegistry::pixmap(const KoID & id) const
{
KisPaintOpFactory* f = value(id.id());
if (!f) {
dbgRegistry << "No paintop" << id.id() << "";
return "";
}
return f->pixmap();
}
QList<KoID> KisPaintOpRegistry::listKeys() const
{
QList<KoID> answer;
foreach(const QString & key, keys()) {
answer.append(KoID(key, get(key)->name()));
}
return answer;
}
diff --git a/krita/image/floodfill/kis_fill_interval.h b/krita/image/floodfill/kis_fill_interval.h
index 4366bdf5e16..77a83aef0c6 100644
--- a/krita/image/floodfill/kis_fill_interval.h
+++ b/krita/image/floodfill/kis_fill_interval.h
@@ -1,74 +1,74 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_FILL_INTERVAL_H
#define __KIS_FILL_INTERVAL_H
#include <boost/operators.hpp>
#include "kis_global.h"
class KisFillInterval : private boost::equality_comparable1<KisFillInterval>
{
public:
KisFillInterval()
: start(0),
end(-1),
row(-1)
{
}
KisFillInterval(int _start, int _end, int _row)
: start(_start),
end(_end),
row(_row)
{
}
inline void invalidate() {
end = start - 1;
}
inline bool operator==(const KisFillInterval &rhs) const {
return start == rhs.start && end == rhs.end && row == rhs.row;
}
inline int width() const {
return end - start + 1;
}
inline bool isValid() const {
return end >= start;
}
int start;
int end;
int row;
};
-#include <QDebug>
+#include <kis_debug.h>
inline QDebug operator<<(QDebug dbg, const KisFillInterval& i)
{
#ifndef NODEBUG
dbg.nospace() << "KisFillInterval(" << i.start << ".." << i.end << "; " << i.row << ")";
#endif
return dbg;
}
#endif /* __KIS_FILL_INTERVAL_H */
diff --git a/krita/image/kis_algebra_2d.cpp b/krita/image/kis_algebra_2d.cpp
index e9d47cf5040..90164b91e2b 100644
--- a/krita/image/kis_algebra_2d.cpp
+++ b/krita/image/kis_algebra_2d.cpp
@@ -1,222 +1,222 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_algebra_2d.h"
#include <QPainterPath>
#include <kis_debug.h>
#define SANITY_CHECKS
namespace KisAlgebra2D {
void adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt)
{
const int numPoints = poly.size();
for (int i = 0; i < numPoints; i++) {
int nextI = i + 1;
if (nextI >= numPoints) {
nextI = 0;
}
const QPointF &p0 = poly[i];
const QPointF &p1 = poly[nextI];
QPointF edge = p1 - p0;
qreal cross = crossProduct(edge, *pt - p0)
/ (0.5 * edge.manhattanLength());
if (cross < 1.0 &&
isInRange(pt->x(), p0.x(), p1.x()) &&
isInRange(pt->y(), p0.y(), p1.y())) {
QPointF salt = 1.0e-3 * inwardUnitNormal(edge, polygonDirection);
QPointF adjustedPoint = *pt + salt;
// in case the polygon is self-intersecting, polygon direction
// might not help
if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
adjustedPoint = *pt - salt;
#ifdef SANITY_CHECKS
if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
- qDebug() << ppVar(*pt);
- qDebug() << ppVar(adjustedPoint);
- qDebug() << ppVar(QLineF(p0, p1));
- qDebug() << ppVar(salt);
+ dbgKrita << ppVar(*pt);
+ dbgKrita << ppVar(adjustedPoint);
+ dbgKrita << ppVar(QLineF(p0, p1));
+ dbgKrita << ppVar(salt);
- qDebug() << ppVar(poly.containsPoint(*pt, Qt::OddEvenFill));
+ dbgKrita << ppVar(poly.containsPoint(*pt, Qt::OddEvenFill));
- qDebug() << ppVar(kisDistanceToLine(*pt, QLineF(p0, p1)));
- qDebug() << ppVar(kisDistanceToLine(adjustedPoint, QLineF(p0, p1)));
+ dbgKrita << ppVar(kisDistanceToLine(*pt, QLineF(p0, p1)));
+ dbgKrita << ppVar(kisDistanceToLine(adjustedPoint, QLineF(p0, p1)));
}
*pt = adjustedPoint;
KIS_ASSERT_RECOVER_NOOP(kisDistanceToLine(*pt, QLineF(p0, p1)) > 1e-4);
#endif /* SANITY_CHECKS */
}
}
}
}
QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2) {
qreal len1 = norm(base1);
if (len1 < 1e-5) return pt;
qreal sin1 = base1.y() / len1;
qreal cos1 = base1.x() / len1;
qreal len2 = norm(base2);
if (len2 < 1e-5) return QPointF();
qreal sin2 = base2.y() / len2;
qreal cos2 = base2.x() / len2;
qreal sinD = sin2 * cos1 - cos2 * sin1;
qreal cosD = cos1 * cos2 + sin1 * sin2;
qreal scaleD = len2 / len1;
QPointF result;
result.rx() = scaleD * (pt.x() * cosD - pt.y() * sinD);
result.ry() = scaleD * (pt.x() * sinD + pt.y() * cosD);
return result;
}
qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
{
qreal a1 = std::atan2(v1.y(), v1.x());
qreal a2 = std::atan2(v2.y(), v2.x());
return a2 - a1;
}
QPainterPath smallArrow()
{
QPainterPath p;
p.moveTo(5, 2);
p.lineTo(-3, 8);
p.lineTo(-5, 5);
p.lineTo( 2, 0);
p.lineTo(-5,-5);
p.lineTo(-3,-8);
p.lineTo( 5,-2);
p.arcTo(QRectF(3, -2, 4, 4), 90, -180);
return p;
}
QRect blowRect(const QRect &rect, qreal coeff)
{
int w = rect.width() * coeff;
int h = rect.height() * coeff;
return rect.adjusted(-w, -h, w, h);
}
template <class Point, class Rect>
inline Point ensureInRectImpl(Point pt, const Rect &bounds)
{
if (pt.x() > bounds.right()) {
pt.rx() = bounds.right();
} else if (pt.x() < bounds.left()) {
pt.rx() = bounds.left();
}
if (pt.y() > bounds.bottom()) {
pt.ry() = bounds.bottom();
} else if (pt.y() < bounds.top()) {
pt.ry() = bounds.top();
}
return pt;
}
QPoint ensureInRect(QPoint pt, const QRect &bounds)
{
return ensureInRectImpl(pt, bounds);
}
QPointF ensureInRect(QPointF pt, const QRectF &bounds)
{
return ensureInRectImpl(pt, bounds);
}
QRect ensureRectNotSmaller(QRect rc, const QSize &size)
{
if (rc.width() < size.width() ||
rc.height() < size.height()) {
int width = qMax(rc.width(), size.width());
int height = qMax(rc.height(), size.height());
rc = QRect(rc.topLeft(), QSize(width, height));
}
return rc;
}
bool intersectLineRect(QLineF &line, const QRect rect)
{
QPointF pt1 = QPointF(), pt2 = QPointF();
QPointF tmp;
if (line.intersect(QLineF(rect.topLeft(), rect.topRight()), &tmp) != QLineF::NoIntersection) {
if (tmp.x() >= rect.left() && tmp.x() <= rect.right()) {
pt1 = tmp;
}
}
if (line.intersect(QLineF(rect.topRight(), rect.bottomRight()), &tmp) != QLineF::NoIntersection) {
if (tmp.y() >= rect.top() && tmp.y() <= rect.bottom()) {
if (pt1.isNull()) pt1 = tmp;
else pt2 = tmp;
}
}
if (line.intersect(QLineF(rect.bottomRight(), rect.bottomLeft()), &tmp) != QLineF::NoIntersection) {
if (tmp.x() >= rect.left() && tmp.x() <= rect.right()) {
if (pt1.isNull()) pt1 = tmp;
else pt2 = tmp;
}
}
if (line.intersect(QLineF(rect.bottomLeft(), rect.topLeft()), &tmp) != QLineF::NoIntersection) {
if (tmp.y() >= rect.top() && tmp.y() <= rect.bottom()) {
if (pt1.isNull()) pt1 = tmp;
else pt2 = tmp;
}
}
if (pt1.isNull() || pt2.isNull()) return false;
// Attempt to retain polarity of end points
if ((line.x1() < line.x2()) != (pt1.x() > pt2.x()) || (line.y1() < line.y2()) != (pt1.y() > pt2.y())) {
tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
line.setP1(pt1);
line.setP2(pt2);
return true;
}
}
diff --git a/krita/image/kis_async_merger.cpp b/krita/image/kis_async_merger.cpp
index 277cf1d73a8..725e0dd7ecb 100644
--- a/krita/image/kis_async_merger.cpp
+++ b/krita/image/kis_async_merger.cpp
@@ -1,353 +1,353 @@
/* Copyright (c) Dmitry Kazakov <dimula73@gmail.com>, 2009
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_async_merger.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QBitArray>
#include <KoChannelInfo.h>
#include <KoCompositeOpRegistry.h>
#include "kis_paint_device.h"
#include "kis_node_visitor.h"
#include "kis_painter.h"
#include "kis_layer.h"
#include "kis_group_layer.h"
#include "kis_adjustment_layer.h"
#include "generator/kis_generator_layer.h"
#include "kis_external_layer_iface.h"
#include "kis_paint_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_clone_layer.h"
#include "kis_processing_information.h"
#include "kis_busy_progress_indicator.h"
#include "kis_merge_walker.h"
#include "kis_refresh_subtree_walker.h"
#include "kis_abstract_projection_plane.h"
//#define DEBUG_MERGER
#ifdef DEBUG_MERGER
#define DEBUG_NODE_ACTION(message, type, leaf, rect) \
- qDebug() << message << type << ":" << leaf->node()->name() << rect
+ dbgKrita << message << type << ":" << leaf->node()->name() << rect
#else
#define DEBUG_NODE_ACTION(message, type, leaf, rect)
#endif
class KisUpdateOriginalVisitor : public KisNodeVisitor
{
public:
KisUpdateOriginalVisitor(QRect updateRect, KisPaintDeviceSP projection, QRect cropRect)
: m_updateRect(updateRect),
m_cropRect(cropRect),
m_projection(projection)
{
}
~KisUpdateOriginalVisitor() {
}
public:
using KisNodeVisitor::visit;
bool visit(KisAdjustmentLayer* layer) {
if (!layer->visible()) return true;
if (!m_projection) {
warnImage << "ObligeChild mechanism has been activated for "
"an adjustment layer! Do nothing...";
layer->original()->clear();
return true;
}
KisPaintDeviceSP originalDevice = layer->original();
originalDevice->clear(m_updateRect);
const QRect applyRect = m_updateRect & m_projection->extent();
// If the intersection of the updaterect and the projection extent is
// null, we are finish here.
if(applyRect.isNull()) return true;
KisSafeFilterConfigurationSP filterConfig = layer->filter();
if (!filterConfig) {
/**
* When an adjustment layer is just created, it may have no
* filter inside. Then the layer has work as a pass-through
* node. Just copy the merged data to the layer's original.
*/
KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
return true;
}
KisSelectionSP selection = layer->fetchComposedInternalSelection(applyRect);
const QRect filterRect = selection ? applyRect & selection->selectedRect() : applyRect;
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
if (!filter) return false;
KisPaintDeviceSP dstDevice = originalDevice;
if (selection) {
dstDevice = new KisPaintDevice(originalDevice->colorSpace());
}
if (!filterRect.isEmpty()) {
KIS_ASSERT_RECOVER_NOOP(layer->busyProgressIndicator());
layer->busyProgressIndicator()->update();
// We do not create a transaction here, as srcDevice != dstDevice
filter->process(m_projection, dstDevice, 0, filterRect, filterConfig.data(), 0);
}
if (selection) {
KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
KisPainter::copyAreaOptimized(filterRect.topLeft(), dstDevice, originalDevice, filterRect, selection);
}
return true;
}
bool visit(KisExternalLayer*) {
return true;
}
bool visit(KisGeneratorLayer*) {
return true;
}
bool visit(KisPaintLayer*) {
return true;
}
bool visit(KisGroupLayer*) {
return true;
}
bool visit(KisCloneLayer *layer) {
QRect emptyRect;
KisRefreshSubtreeWalker walker(emptyRect);
KisAsyncMerger merger;
KisLayerSP srcLayer = layer->copyFrom();
QRect srcRect = m_updateRect.translated(-layer->x(), -layer->y());
QRegion prepareRegion(srcRect);
prepareRegion -= m_cropRect;
/**
* If a clone has complicated masks, we should prepare additional
* source area to ensure the rect is prepared.
*/
QRect needRectOnSource = layer->needRectOnSourceForMasks(srcRect);
if (!needRectOnSource.isEmpty()) {
prepareRegion += needRectOnSource;
}
foreach(const QRect &rect, prepareRegion.rects()) {
walker.collectRects(srcLayer, rect);
merger.startMerge(walker, false);
}
return true;
}
bool visit(KisNode*) {
return true;
}
bool visit(KisFilterMask*) {
return true;
}
bool visit(KisTransformMask*) {
return true;
}
bool visit(KisTransparencyMask*) {
return true;
}
bool visit(KisSelectionMask*) {
return true;
}
private:
QRect m_updateRect;
QRect m_cropRect;
KisPaintDeviceSP m_projection;
};
/*********************************************************************/
/* KisAsyncMerger */
/*********************************************************************/
void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
KisMergeWalker::LeafStack &leafStack = walker.leafStack();
const bool useTempProjections = walker.needRectVaries();
while(!leafStack.isEmpty()) {
KisMergeWalker::JobItem item = leafStack.pop();
KisProjectionLeafSP currentLeaf = item.m_leaf;
if(currentLeaf->isRoot()) continue;
// All the masks should be filtered by the walkers
Q_ASSERT(currentLeaf->isLayer());
QRect applyRect = item.m_applyRect;
if(item.m_position & KisMergeWalker::N_EXTRA) {
// The type of layers that will not go to projection.
DEBUG_NODE_ACTION("Updating", "N_EXTRA", currentLeaf, applyRect);
KisUpdateOriginalVisitor originalVisitor(applyRect,
m_currentProjection,
walker.cropRect());
currentLeaf->accept(originalVisitor);
currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node());
continue;
}
if(!m_currentProjection)
setupProjection(currentLeaf, applyRect, useTempProjections);
KisUpdateOriginalVisitor originalVisitor(applyRect,
m_currentProjection,
walker.cropRect());
if(item.m_position & KisMergeWalker::N_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentLeaf, applyRect);
currentLeaf->accept(originalVisitor);
currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode());
}
else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentLeaf, applyRect);
if(currentLeaf->dependsOnLowerNodes()) {
currentLeaf->accept(originalVisitor);
currentLeaf->projectionPlane()->recalculate(applyRect, currentLeaf->node());
}
}
else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentLeaf, applyRect);
currentLeaf->projectionPlane()->recalculate(applyRect, walker.startNode());
}
else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ {
DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentLeaf, applyRect);
/* nothing to do */
}
compositeWithProjection(currentLeaf, applyRect);
if(item.m_position & KisMergeWalker::N_TOPMOST) {
writeProjection(currentLeaf, useTempProjections, applyRect);
resetProjection();
}
}
if(notifyClones) {
doNotifyClones(walker);
}
if(m_currentProjection) {
warnImage << "BUG: The walker hasn't reached the root layer!";
warnImage << " Start node:" << walker.startNode() << "Requested rect:" << walker.requestedRect();
warnImage << " There must be an inconsistency in the walkers happened!";
warnImage << " Please report a bug describing how you got this message.";
// reset projection to avoid artefacts in next merges and allow people to work further
resetProjection();
}
}
void KisAsyncMerger::resetProjection() {
m_currentProjection = 0;
m_finalProjection = 0;
}
void KisAsyncMerger::setupProjection(KisProjectionLeafSP currentLeaf, const QRect& rect, bool useTempProjection) {
KisPaintDeviceSP parentOriginal = currentLeaf->parent()->original();
if (parentOriginal != currentLeaf->projection()) {
if (useTempProjection) {
if(!m_cachedPaintDevice)
m_cachedPaintDevice = new KisPaintDevice(parentOriginal->colorSpace());
m_currentProjection = m_cachedPaintDevice;
m_currentProjection->prepareClone(parentOriginal);
m_finalProjection = parentOriginal;
}
else {
parentOriginal->clear(rect);
m_finalProjection = m_currentProjection = parentOriginal;
}
}
else {
/**
* It happened so that our parent uses our own projection as
* its original. It means obligeChild mechanism works.
* We won't initialise m_currentProjection. This will cause
* writeProjection() and compositeWithProjection() do nothing
* when called.
*/
/* NOP */
}
}
void KisAsyncMerger::writeProjection(KisProjectionLeafSP topmostLeaf, bool useTempProjection, QRect rect) {
Q_UNUSED(useTempProjection);
Q_UNUSED(topmostLeaf);
if (!m_currentProjection) return;
if(m_currentProjection != m_finalProjection) {
KisPainter::copyAreaOptimized(rect.topLeft(), m_currentProjection, m_finalProjection, rect);
}
DEBUG_NODE_ACTION("Writing projection", "", topmostLeaf->parent(), rect);
}
bool KisAsyncMerger::compositeWithProjection(KisProjectionLeafSP leaf, const QRect &rect) {
if (!m_currentProjection) return true;
if (!leaf->visible()) return true;
KisPainter gc(m_currentProjection);
leaf->projectionPlane()->apply(&gc, rect);
DEBUG_NODE_ACTION("Compositing projection", "", leaf, rect);
return true;
}
void KisAsyncMerger::doNotifyClones(KisBaseRectsWalker &walker) {
KisBaseRectsWalker::CloneNotificationsVector &vector =
walker.cloneNotifications();
KisBaseRectsWalker::CloneNotificationsVector::iterator it;
for(it = vector.begin(); it != vector.end(); ++it) {
(*it).notify();
}
}
diff --git a/krita/image/kis_base_rects_walker.h b/krita/image/kis_base_rects_walker.h
index 7c8b0fae463..c632c307d36 100644
--- a/krita/image/kis_base_rects_walker.h
+++ b/krita/image/kis_base_rects_walker.h
@@ -1,456 +1,456 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_BASE_RECTS_WALKER_H
#define __KIS_BASE_RECTS_WALKER_H
#include <QStack>
#include "kis_layer.h"
#include "kis_mask.h"
#include "kis_abstract_projection_plane.h"
#include "kis_projection_leaf.h"
class KisBaseRectsWalker;
typedef KisSharedPtr<KisBaseRectsWalker> KisBaseRectsWalkerSP;
class KRITAIMAGE_EXPORT KisBaseRectsWalker : public KisShared
{
public:
enum UpdateType {
UPDATE,
UPDATE_NO_FILTHY,
FULL_REFRESH,
UNSUPPORTED
};
typedef qint32 NodePosition;
enum NodePositionValues {
/**
* There are two different sets of values.
* The first describes the position of the node to the graph,
* the second shows the position to the filthy node
*/
N_NORMAL = 0x00,
N_TOPMOST = 0x01,
N_BOTTOMMOST = 0x02,
N_EXTRA = 0x04,
N_ABOVE_FILTHY = 0x08,
N_FILTHY_ORIGINAL = 0x10, // not used actually
N_FILTHY_PROJECTION = 0x20,
N_FILTHY = 0x40,
N_BELOW_FILTHY = 0x80
};
#define GRAPH_POSITION_MASK 0x07
static inline KisNode::PositionToFilthy convertPositionToFilthy(NodePosition position) {
static const int positionToFilthyMask =
N_ABOVE_FILTHY |
N_FILTHY_PROJECTION |
N_FILTHY |
N_BELOW_FILTHY;
qint32 positionToFilthy = position & N_EXTRA ? N_FILTHY : position & positionToFilthyMask;
// We do not use N_FILTHY_ORIGINAL yet, so...
Q_ASSERT(positionToFilthy);
return static_cast<KisNode::PositionToFilthy>(positionToFilthy);
}
struct CloneNotification {
CloneNotification() {}
CloneNotification(KisNodeSP node, const QRect &dirtyRect)
: m_layer(qobject_cast<KisLayer*>(node.data())),
m_dirtyRect(dirtyRect) {}
void notify() {
Q_ASSERT(m_layer); // clones are possible for layers only
m_layer->updateClones(m_dirtyRect);
}
private:
friend class KisWalkersTest;
KisLayerSP m_layer;
QRect m_dirtyRect;
};
typedef QVector<CloneNotification> CloneNotificationsVector;
struct JobItem {
KisProjectionLeafSP m_leaf;
NodePosition m_position;
/**
* The rect that should be prepared on this node.
* E.g. area where the filter applies on filter layer
* or an area of a paint layer that will be copied to
* the projection.
*/
QRect m_applyRect;
};
typedef QStack<JobItem> LeafStack;
public:
virtual ~KisBaseRectsWalker() {
}
void collectRects(KisNodeSP node, const QRect& requestedRect) {
clear();
KisProjectionLeafSP startLeaf = node->projectionLeaf();
m_nodeChecksum = calculateChecksum(startLeaf, requestedRect);
m_graphChecksum = node->graphSequenceNumber();
m_resultChangeRect = requestedRect;
m_resultUncroppedChangeRect = requestedRect;
m_requestedRect = requestedRect;
m_startNode = node;
startTrip(startLeaf);
}
inline void recalculate(const QRect& requestedRect) {
Q_ASSERT(m_startNode);
KisProjectionLeafSP startLeaf = m_startNode->projectionLeaf();
if(startLeaf->isStillInGraph()) {
collectRects(m_startNode, requestedRect);
}
else {
clear();
m_nodeChecksum = calculateChecksum(startLeaf, requestedRect);
m_graphChecksum = m_startNode->graphSequenceNumber();
m_resultChangeRect = QRect();
m_resultUncroppedChangeRect = QRect();
}
}
bool checksumValid() {
Q_ASSERT(m_startNode);
return
m_nodeChecksum == calculateChecksum(m_startNode->projectionLeaf(), m_requestedRect) &&
m_graphChecksum == m_startNode->graphSequenceNumber();
}
inline void setCropRect(QRect cropRect) {
m_cropRect = cropRect;
}
inline QRect cropRect() const{
return m_cropRect;
}
// return a reference for efficiency reasons
inline LeafStack& leafStack() {
return m_mergeTask;
}
// return a reference for efficiency reasons
inline CloneNotificationsVector& cloneNotifications() {
return m_cloneNotifications;
}
inline QRect accessRect() const {
return m_resultAccessRect;
}
inline QRect changeRect() const {
return m_resultChangeRect;
}
inline QRect uncroppedChangeRect() const {
return m_resultUncroppedChangeRect;
}
inline bool needRectVaries() const {
return m_needRectVaries;
}
inline bool changeRectVaries() const {
return m_changeRectVaries;
}
inline KisNodeSP startNode() const {
return m_startNode;
}
inline QRect requestedRect() const {
return m_requestedRect;
}
virtual UpdateType type() const = 0;
protected:
/**
* Initiates collecting of rects.
* Should be implemented in derived classes
*/
virtual void startTrip(KisProjectionLeafSP startWith) = 0;
protected:
static inline qint32 getGraphPosition(qint32 position) {
return position & GRAPH_POSITION_MASK;
}
static inline bool hasClones(KisNodeSP node) {
KisLayer *layer = qobject_cast<KisLayer*>(node.data());
return layer && layer->hasClones();
}
static inline NodePosition calculateNodePosition(KisProjectionLeafSP leaf) {
KisProjectionLeafSP nextLeaf = leaf->nextSibling();
while(nextLeaf && !nextLeaf->isLayer()) nextLeaf = nextLeaf->nextSibling();
if (!nextLeaf) return N_TOPMOST;
KisProjectionLeafSP prevLeaf = leaf->prevSibling();
while(prevLeaf && !prevLeaf->isLayer()) prevLeaf = prevLeaf->prevSibling();
if (!prevLeaf) return N_BOTTOMMOST;
return N_NORMAL;
}
inline bool isStartLeaf(KisProjectionLeafSP leaf) const {
return leaf->node() == m_startNode;
}
inline void clear() {
m_resultAccessRect = m_resultNeedRect = /*m_resultChangeRect =*/
m_childNeedRect = m_lastNeedRect = QRect();
m_needRectVaries = m_changeRectVaries = false;
m_mergeTask.clear();
m_cloneNotifications.clear();
// Not needed really. Think over removing.
//m_startNode = 0;
//m_requestedRect = QRect();
}
inline void pushJob(KisProjectionLeafSP leaf, NodePosition position, QRect applyRect) {
JobItem item = {leaf, position, applyRect};
m_mergeTask.push(item);
}
inline QRect cropThisRect(const QRect& rect) {
return m_cropRect.isValid() ? rect & m_cropRect : rect;
}
/**
* Used by KisFullRefreshWalker as it has a special changeRect strategy
*/
inline void setExplicitChangeRect(KisProjectionLeafSP leaf, const QRect &changeRect, bool changeRectVaries) {
m_resultChangeRect = changeRect;
m_resultUncroppedChangeRect = changeRect;
m_changeRectVaries = changeRectVaries;
registerCloneNotification(leaf->node(), N_FILTHY);
}
/**
* Called for every node we meet on a forward way of the trip.
*/
virtual void registerChangeRect(KisProjectionLeafSP leaf, NodePosition position) {
// We do not work with masks here. It is KisLayer's job.
if(!leaf->isLayer()) return;
if(!leaf->visible()) return;
QRect currentChangeRect = leaf->projectionPlane()->changeRect(m_resultChangeRect,
convertPositionToFilthy(position));
currentChangeRect = cropThisRect(currentChangeRect);
if(!m_changeRectVaries)
m_changeRectVaries = currentChangeRect != m_resultChangeRect;
m_resultChangeRect = currentChangeRect;
m_resultUncroppedChangeRect = leaf->projectionPlane()->changeRect(m_resultUncroppedChangeRect,
convertPositionToFilthy(position));
registerCloneNotification(leaf->node(), position);
}
void registerCloneNotification(KisNodeSP node, NodePosition position) {
/**
* Note, we do not check for (N_ABOVE_FILTHY &&
* dependOnLowerNodes(node)) because it may lead to an
* infinite loop with filter layer. Activate it when it is
* guaranteed that it is not possible to create a filter layer
* avobe its own clone
*/
if(hasClones(node) && position & (N_FILTHY | N_FILTHY_PROJECTION)) {
m_cloneNotifications.append(
CloneNotification(node, m_resultUncroppedChangeRect));
}
}
/**
* Called for every node we meet on a backward way of the trip.
*/
virtual void registerNeedRect(KisProjectionLeafSP leaf, NodePosition position) {
// We do not work with masks here. It is KisLayer's job.
if(!leaf->isLayer()) return;
if(m_mergeTask.isEmpty())
m_resultAccessRect = m_resultNeedRect = m_childNeedRect =
m_lastNeedRect = m_resultChangeRect;
QRect currentNeedRect;
if(position & N_TOPMOST)
m_lastNeedRect = m_childNeedRect;
if (!leaf->visible()) {
if (!m_lastNeedRect.isEmpty()) {
// push a dumb job to fit state machine requirements
pushJob(leaf, position, m_lastNeedRect);
}
} else if(position & (N_FILTHY | N_ABOVE_FILTHY | N_EXTRA)) {
if(!m_lastNeedRect.isEmpty())
pushJob(leaf, position, m_lastNeedRect);
//else /* Why push empty rect? */;
m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
convertPositionToFilthy(position));
m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
convertPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
m_childNeedRect = m_lastNeedRect;
}
else if(position & (N_BELOW_FILTHY | N_FILTHY_PROJECTION)) {
if(!m_lastNeedRect.isEmpty()) {
pushJob(leaf, position, m_lastNeedRect);
m_resultAccessRect |= leaf->projectionPlane()->accessRect(m_lastNeedRect,
convertPositionToFilthy(position));
m_lastNeedRect = leaf->projectionPlane()->needRect(m_lastNeedRect,
convertPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
}
}
else {
// N_FILTHY_ORIGINAL is not used so it goes there
qFatal("KisBaseRectsWalker: node position(%d) is out of range", position);
}
if(!m_needRectVaries)
m_needRectVaries = m_resultNeedRect != m_lastNeedRect;
m_resultNeedRect |= m_lastNeedRect;
}
virtual void adjustMasksChangeRect(KisProjectionLeafSP firstMask) {
KisProjectionLeafSP currentLeaf = firstMask;
while (currentLeaf) {
/**
* ATTENTION: we miss the first mask
*/
do {
currentLeaf = currentLeaf->nextSibling();
} while (currentLeaf &&
(!currentLeaf->isMask() || !currentLeaf->visible()));
if(currentLeaf) {
QRect changeRect = currentLeaf->projectionPlane()->changeRect(m_resultChangeRect);
m_changeRectVaries |= changeRect != m_resultChangeRect;
m_resultChangeRect = changeRect;
m_resultUncroppedChangeRect = changeRect;
}
}
KisProjectionLeafSP parentLayer = firstMask->parent();
Q_ASSERT(parentLayer);
registerCloneNotification(parentLayer->node(), N_FILTHY_PROJECTION);
}
static qint32 calculateChecksum(KisProjectionLeafSP leaf, const QRect &requestedRect) {
qint32 checksum = 0;
qint32 x, y, w, h;
QRect tempRect;
tempRect = leaf->projectionPlane()->changeRect(requestedRect);
tempRect.getRect(&x, &y, &w, &h);
checksum += -x - y + w + h;
tempRect = leaf->projectionPlane()->needRect(requestedRect);
tempRect.getRect(&x, &y, &w, &h);
checksum += -x - y + w + h;
-// qCritical() << leaf << requestedRect << "-->" << checksum;
+// errKrita << leaf << requestedRect << "-->" << checksum;
return checksum;
}
private:
/**
* The result variables.
* By the end of a recursion they will store a complete
* data for a successful merge operation.
*/
QRect m_resultAccessRect;
QRect m_resultNeedRect;
QRect m_resultChangeRect;
QRect m_resultUncroppedChangeRect;
bool m_needRectVaries;
bool m_changeRectVaries;
LeafStack m_mergeTask;
CloneNotificationsVector m_cloneNotifications;
/**
* Used by update optimization framework
*/
KisNodeSP m_startNode;
QRect m_requestedRect;
/**
* Used for getting know whether the start node
* properties have changed since the walker was
* calculated
*/
qint32 m_nodeChecksum;
/**
* Used for getting know whether the structure of
* the graph has changed since the walker was
* calculated
*/
qint32 m_graphChecksum;
/**
* Temporary variables
*/
QRect m_cropRect;
QRect m_childNeedRect;
QRect m_lastNeedRect;
};
#endif /* __KIS_BASE_RECTS_WALKER_H */
diff --git a/krita/image/kis_brush_mask_applicator_factories.cpp b/krita/image/kis_brush_mask_applicator_factories.cpp
index 0817e50c623..f5971ab74e6 100644
--- a/krita/image/kis_brush_mask_applicator_factories.cpp
+++ b/krita/image/kis_brush_mask_applicator_factories.cpp
@@ -1,121 +1,121 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_brush_mask_applicator_factories.h"
#include "kis_circle_mask_generator.h"
#include "kis_circle_mask_generator_p.h"
#include "kis_brush_mask_applicators.h"
#define a(_s) #_s
#define b(_s) a(_s)
template<>
template<>
MaskApplicatorFactory<KisMaskGenerator, KisBrushMaskScalarApplicator>::ReturnType
MaskApplicatorFactory<KisMaskGenerator, KisBrushMaskScalarApplicator>::create<VC_IMPL>(ParamType maskGenerator)
{
- // qDebug() << "Creating scalar applicator" << b(VC_IMPL);
+ // dbgKrita << "Creating scalar applicator" << b(VC_IMPL);
return new KisBrushMaskScalarApplicator<KisMaskGenerator,VC_IMPL>(maskGenerator);
}
template<>
template<>
MaskApplicatorFactory<KisCircleMaskGenerator, KisBrushMaskVectorApplicator>::ReturnType
MaskApplicatorFactory<KisCircleMaskGenerator, KisBrushMaskVectorApplicator>::create<VC_IMPL>(ParamType maskGenerator)
{
- // qDebug() << "Creating vector applicator" << b(VC_IMPL);
+ // dbgKrita << "Creating vector applicator" << b(VC_IMPL);
return new KisBrushMaskVectorApplicator<KisCircleMaskGenerator,VC_IMPL>(maskGenerator);
}
#if defined HAVE_VC
struct KisCircleMaskGenerator::FastRowProcessor
{
FastRowProcessor(KisCircleMaskGenerator *maskGenerator)
: d(maskGenerator->d) {}
template<Vc::Implementation _impl>
void process(float* buffer, int width, float y, float cosa, float sina,
float centerX, float centerY);
KisCircleMaskGenerator::Private *d;
};
template<> void KisCircleMaskGenerator::
FastRowProcessor::process<VC_IMPL>(float* buffer, int width, float y, float cosa, float sina,
float centerX, float centerY)
{
const bool useSmoothing = d->copyOfAntialiasEdges;
float y_ = y - centerY;
float sinay_ = sina * y_;
float cosay_ = cosa * y_;
float* bufferPointer = buffer;
Vc::float_v currentIndices(Vc::int_v::IndexesFromZero());
Vc::float_v increment((float)Vc::float_v::Size);
Vc::float_v vCenterX(centerX);
Vc::float_v vCosa(cosa);
Vc::float_v vSina(sina);
Vc::float_v vCosaY_(cosay_);
Vc::float_v vSinaY_(sinay_);
Vc::float_v vXCoeff(d->xcoef);
Vc::float_v vYCoeff(d->ycoef);
Vc::float_v vTransformedFadeX(d->transformedFadeX);
Vc::float_v vTransformedFadeY(d->transformedFadeY);
Vc::float_v vOne(1.0f);
for (int i=0; i < width; i+= Vc::float_v::Size){
Vc::float_v x_ = currentIndices - vCenterX;
Vc::float_v xr = x_ * vCosa - vSinaY_;
Vc::float_v yr = x_ * vSina + vCosaY_;
Vc::float_v n = pow2(xr * vXCoeff) + pow2(yr * vYCoeff);
if (useSmoothing) {
xr = Vc::abs(xr) + vOne;
yr = Vc::abs(yr) + vOne;
}
Vc::float_v vNormFade = pow2(xr * vTransformedFadeX) + pow2(yr * vTransformedFadeY);
//255 * n * (normeFade - 1) / (normeFade - n)
Vc::float_v vFade = n * (vNormFade - vOne) / (vNormFade - n);
// Mask out the inner circe of the mask
Vc::float_m mask = vNormFade < vOne;
vFade.setZero(mask);
vFade = Vc::min(vFade, vOne);
vFade.store(bufferPointer);
currentIndices = currentIndices + increment;
bufferPointer += Vc::float_v::Size;
}
}
#endif /* defined HAVE_VC */
diff --git a/krita/image/kis_cached_gradient_shape_strategy.cpp b/krita/image/kis_cached_gradient_shape_strategy.cpp
index df12426c3ce..fde795ff2fe 100644
--- a/krita/image/kis_cached_gradient_shape_strategy.cpp
+++ b/krita/image/kis_cached_gradient_shape_strategy.cpp
@@ -1,100 +1,100 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_cached_gradient_shape_strategy.h"
#include <QRect>
#include "bsplines/kis_bspline_2d.h"
#include <cmath>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include "kis_algebra_2d.h"
#include "kis_debug.h"
using namespace KisBSplines;
struct Q_DECL_HIDDEN KisCachedGradientShapeStrategy::Private
{
QRect rc;
qreal xStep;
qreal yStep;
QScopedPointer<KisGradientShapeStrategy> baseStrategy;
QScopedPointer<KisBSpline2D> spline;
};
KisCachedGradientShapeStrategy::KisCachedGradientShapeStrategy(const QRect &rc,
qreal xStep,
qreal yStep,
KisGradientShapeStrategy *baseStrategy)
: KisGradientShapeStrategy(),
m_d(new Private())
{
KIS_ASSERT_RECOVER_NOOP(rc.width() >= 3 && rc.height() >= 3);
m_d->rc = rc;
m_d->xStep = xStep;
m_d->yStep = yStep;
m_d->baseStrategy.reset(baseStrategy);
qreal xStart = rc.x();
qreal yStart = rc.y();
qreal xEnd = rc.x() + rc.width();
qreal yEnd = rc.y() + rc.height();
int numSamplesX = std::ceil(qreal(rc.width()) / xStep);
int numSamplesY = std::ceil(qreal(rc.height()) / yStep);
if (numSamplesX < 2 || numSamplesY < 2) {
- qWarning();
- qWarning() << "############";
- qWarning() << "WARNING: KisCachedGradientShapeStrategy numSamplesX/Y is too small!" << ppVar(numSamplesX) << ppVar(numSamplesY);
- qWarning() << "WARNING:" << ppVar(rc) << ppVar(xStep) << ppVar(yStep);
- qWarning() << "WARNING:" << ppVar(numSamplesX) << ppVar(numSamplesY);
+ warnKrita;
+ warnKrita << "############";
+ warnKrita << "WARNING: KisCachedGradientShapeStrategy numSamplesX/Y is too small!" << ppVar(numSamplesX) << ppVar(numSamplesY);
+ warnKrita << "WARNING:" << ppVar(rc) << ppVar(xStep) << ppVar(yStep);
+ warnKrita << "WARNING:" << ppVar(numSamplesX) << ppVar(numSamplesY);
numSamplesX = qMax(numSamplesX, 2);
numSamplesY = qMax(numSamplesY, 2);
- qWarning() << "WARNING: adjusting:" << ppVar(numSamplesX) << ppVar(numSamplesY);
- qWarning() << "############";
- qWarning();
+ warnKrita << "WARNING: adjusting:" << ppVar(numSamplesX) << ppVar(numSamplesY);
+ warnKrita << "############";
+ warnKrita;
}
m_d->spline.reset(new KisBSpline2D(xStart, xEnd, numSamplesX, Natural,
yStart, yEnd, numSamplesY, Natural));
boost::function<qreal(qreal, qreal)> valueOp =
boost::bind(&KisGradientShapeStrategy::valueAt, m_d->baseStrategy.data(), _1, _2);
m_d->spline->initializeSpline(valueOp);
}
KisCachedGradientShapeStrategy::~KisCachedGradientShapeStrategy()
{
}
double KisCachedGradientShapeStrategy::valueAt(double x, double y) const
{
QPointF pt = KisAlgebra2D::ensureInRect(QPointF(x, y), m_d->rc);
return m_d->spline->value(pt.x(), pt.y());
}
diff --git a/krita/image/kis_cage_transform_worker.cpp b/krita/image/kis_cage_transform_worker.cpp
index dce7b1b95e9..9c9bb408ec1 100644
--- a/krita/image/kis_cage_transform_worker.cpp
+++ b/krita/image/kis_cage_transform_worker.cpp
@@ -1,397 +1,397 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_cage_transform_worker.h"
#include "kis_grid_interpolation_tools.h"
#include "kis_green_coordinates_math.h"
#include <QPainter>
#include "KoColor.h"
#include "kis_selection.h"
#include "kis_painter.h"
#ifdef Q_OS_WIN
#include <float.h>
#ifndef __MINGW32__
#define isnan _isnan
#endif
#endif
struct Q_DECL_HIDDEN KisCageTransformWorker::Private
{
Private(KisPaintDeviceSP _dev,
const QVector<QPointF> &_origCage,
KoUpdater *_progress,
int _pixelPrecision)
: dev(_dev),
origCage(_origCage),
progress(_progress),
pixelPrecision(_pixelPrecision)
{
}
KisPaintDeviceSP dev;
QImage srcImage;
QPointF srcImageOffset;
QVector<QPointF> origCage;
QVector<QPointF> transfCage;
KoUpdater *progress;
int pixelPrecision;
QVector<int> allToValidPointsMap;
QVector<QPointF> validPoints;
/**
* Contains all points fo the grid including non-defined
* points (the ones which are placed outside the cage).
*/
QVector<QPointF> allSrcPoints;
KisGreenCoordinatesMath cage;
QSize gridSize;
bool isGridEmpty() const {
return allSrcPoints.isEmpty();
}
QVector<QPointF> calculateTransformedPoints();
inline QVector<int> calculateMappedIndexes(int col, int row,
int *numExistingPoints);
int tryGetValidIndex(const QPoint &cellPt);
struct MapIndexesOp;
};
KisCageTransformWorker::KisCageTransformWorker(KisPaintDeviceSP dev,
const QVector<QPointF> &origCage,
KoUpdater *progress,
int pixelPrecision)
: m_d(new Private(dev, origCage, progress, pixelPrecision))
{
}
KisCageTransformWorker::KisCageTransformWorker(const QImage &srcImage,
const QPointF &srcImageOffset,
const QVector<QPointF> &origCage,
KoUpdater *progress,
int pixelPrecision)
: m_d(new Private(0, origCage, progress, pixelPrecision))
{
m_d->srcImage = srcImage;
m_d->srcImageOffset = srcImageOffset;
}
KisCageTransformWorker::~KisCageTransformWorker()
{
}
void KisCageTransformWorker::setTransformedCage(const QVector<QPointF> &transformedCage)
{
m_d->transfCage = transformedCage;
}
struct PointsFetcherOp
{
PointsFetcherOp(const QPolygonF &cagePolygon)
: m_cagePolygon(cagePolygon),
m_numValidPoints(0)
{
m_polygonDirection = KisAlgebra2D::polygonDirection(cagePolygon);
}
inline void processPoint(int col, int row,
int prevCol, int prevRow,
int colIndex, int rowIndex) {
Q_UNUSED(prevCol);
Q_UNUSED(prevRow);
Q_UNUSED(colIndex);
Q_UNUSED(rowIndex);
QPointF pt(col, row);
if (m_cagePolygon.containsPoint(pt, Qt::OddEvenFill)) {
KisAlgebra2D::adjustIfOnPolygonBoundary(m_cagePolygon, m_polygonDirection, &pt);
m_points << pt;
m_pointValid << true;
m_numValidPoints++;
} else {
m_points << pt;
m_pointValid << false;
}
}
inline void nextLine() {
}
QVector<bool> m_pointValid;
QVector<QPointF> m_points;
QPolygonF m_cagePolygon;
int m_polygonDirection;
int m_numValidPoints;
};
void KisCageTransformWorker::prepareTransform()
{
if (m_d->origCage.size() < 3) return;
const QPolygonF srcPolygon(m_d->origCage);
QRect srcBounds = m_d->dev ? m_d->dev->region().boundingRect() :
QRectF(m_d->srcImageOffset, m_d->srcImage.size()).toAlignedRect();
srcBounds &= srcPolygon.boundingRect().toAlignedRect();
// no need to process empty devices
if (srcBounds.isEmpty()) return;
m_d->gridSize =
GridIterationTools::calcGridSize(srcBounds, m_d->pixelPrecision);
PointsFetcherOp pointsOp(srcPolygon);
GridIterationTools::processGrid(pointsOp, srcBounds, m_d->pixelPrecision);
const int numPoints = pointsOp.m_points.size();
KIS_ASSERT_RECOVER_RETURN(numPoints == m_d->gridSize.width() * m_d->gridSize.height());
m_d->allSrcPoints = pointsOp.m_points;
m_d->allToValidPointsMap.resize(pointsOp.m_points.size());
m_d->validPoints.resize(pointsOp.m_numValidPoints);
{
int validIdx = 0;
for (int i = 0; i < numPoints; i++) {
const QPointF &pt = pointsOp.m_points[i];
const bool pointValid = pointsOp.m_pointValid[i];
if (pointValid) {
m_d->validPoints[validIdx] = pt;
m_d->allToValidPointsMap[i] = validIdx;
validIdx++;
} else {
m_d->allToValidPointsMap[i] = -1;
}
}
KIS_ASSERT_RECOVER_NOOP(validIdx == m_d->validPoints.size());
}
m_d->cage.precalculateGreenCoordinates(m_d->origCage, m_d->validPoints);
}
QVector<QPointF> KisCageTransformWorker::Private::calculateTransformedPoints()
{
cage.generateTransformedCageNormals(transfCage);
const int numValidPoints = validPoints.size();
QVector<QPointF> transformedPoints(numValidPoints);
for (int i = 0; i < numValidPoints; i++) {
transformedPoints[i] = cage.transformedPoint(i, transfCage);
#ifdef Q_CC_MSVC
if (isnan(transformedPoints[i].x()) ||
isnan(transformedPoints[i].y())) {
#else
if (std::isnan(transformedPoints[i].x()) ||
std::isnan(transformedPoints[i].y())) {
#endif
- qWarning() << "WARNING: One grid point has been removed from a consideration" << validPoints[i];
+ warnKrita << "WARNING: One grid point has been removed from a consideration" << validPoints[i];
transformedPoints[i] = validPoints[i];
}
}
return transformedPoints;
}
inline QVector<int> KisCageTransformWorker::Private::
calculateMappedIndexes(int col, int row,
int *numExistingPoints)
{
*numExistingPoints = 0;
QVector<int> cellIndexes =
GridIterationTools::calculateCellIndexes(col, row, gridSize);
for (int i = 0; i < 4; i++) {
cellIndexes[i] = allToValidPointsMap[cellIndexes[i]];
*numExistingPoints += cellIndexes[i] >= 0;
}
return cellIndexes;
}
int KisCageTransformWorker::Private::
tryGetValidIndex(const QPoint &cellPt)
{
int index = -1;
return
cellPt.x() >= 0 &&
cellPt.y() >= 0 &&
cellPt.x() < gridSize.width() - 1 &&
cellPt.y() < gridSize.height() - 1 &&
(index = allToValidPointsMap[GridIterationTools::pointToIndex(cellPt, gridSize)]) >= 0, index;
}
struct KisCageTransformWorker::Private::MapIndexesOp {
MapIndexesOp(KisCageTransformWorker::Private *d)
: m_d(d),
m_srcCagePolygon(QPolygonF(m_d->origCage))
{
}
inline QVector<int> calculateMappedIndexes(int col, int row,
int *numExistingPoints) const {
return m_d->calculateMappedIndexes(col, row, numExistingPoints);
}
inline int tryGetValidIndex(const QPoint &cellPt) const {
return m_d->tryGetValidIndex(cellPt);
}
inline QPointF getSrcPointForce(const QPoint &cellPt) const {
return m_d->allSrcPoints[GridIterationTools::pointToIndex(cellPt, m_d->gridSize)];
}
inline const QPolygonF srcCropPolygon() const {
return m_srcCagePolygon;
}
KisCageTransformWorker::Private *m_d;
QPolygonF m_srcCagePolygon;
};
void KisCageTransformWorker::run()
{
if (m_d->isGridEmpty()) return;
KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() >= 3);
KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() == m_d->transfCage.size());
QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
KisPaintDeviceSP srcDev = new KisPaintDevice(*m_d->dev.data());
KisPaintDeviceSP tempDevice = new KisPaintDevice(m_d->dev->colorSpace());
{
KisSelectionSP selection = new KisSelection();
KisPainter painter(selection->pixelSelection());
painter.setPaintColor(KoColor(Qt::black, selection->pixelSelection()->colorSpace()));
painter.setAntiAliasPolygonFill(true);
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
painter.setStrokeStyle(KisPainter::StrokeStyleNone);
painter.paintPolygon(m_d->origCage);
m_d->dev->clearSelection(selection);
}
GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, tempDevice);
Private::MapIndexesOp indexesOp(m_d.data());
GridIterationTools::iterateThroughGrid
<GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
m_d->validPoints,
transformedPoints);
QRect rect = tempDevice->extent();
KisPainter gc(m_d->dev);
gc.bitBlt(rect.topLeft(), tempDevice, rect);
}
QImage KisCageTransformWorker::runOnQImage(QPointF *newOffset)
{
if (m_d->isGridEmpty()) QImage();
KIS_ASSERT_RECOVER(m_d->origCage.size() >= 3 &&
m_d->origCage.size() == m_d->transfCage.size()) {
return QImage();
}
KIS_ASSERT_RECOVER(!m_d->srcImage.isNull()) {
return QImage();
}
KIS_ASSERT_RECOVER(m_d->srcImage.format() == QImage::Format_ARGB32) {
return QImage();
}
QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
QRectF dstBounds;
foreach (const QPointF &pt, transformedPoints) {
KisAlgebra2D::accumulateBounds(pt, &dstBounds);
}
const QRectF srcBounds(m_d->srcImageOffset, m_d->srcImage.size());
dstBounds |= srcBounds;
QPointF dstQImageOffset = dstBounds.topLeft();
*newOffset = dstQImageOffset;
QRect dstBoundsI = dstBounds.toAlignedRect();
QImage dstImage(dstBoundsI.size(), m_d->srcImage.format());
dstImage.fill(0);
QImage tempImage(dstImage);
{
// we shouldn't create too many painters
QPainter gc(&dstImage);
gc.drawImage(-dstQImageOffset + m_d->srcImageOffset, m_d->srcImage);
gc.setBrush(Qt::black);
gc.setPen(Qt::black);
gc.setCompositionMode(QPainter::CompositionMode_Clear);
gc.drawPolygon(QPolygonF(m_d->origCage).translated(-dstQImageOffset));
gc.end();
}
GridIterationTools::QImagePolygonOp polygonOp(m_d->srcImage, tempImage, m_d->srcImageOffset, dstQImageOffset);
Private::MapIndexesOp indexesOp(m_d.data());
GridIterationTools::iterateThroughGrid
<GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
m_d->validPoints,
transformedPoints);
{
QPainter gc(&dstImage);
gc.drawImage(QPoint(), tempImage);
}
return dstImage;
}
diff --git a/krita/image/kis_config_widget.cpp b/krita/image/kis_config_widget.cpp
index ddb597f2360..f90f2c3cde5 100644
--- a/krita/image/kis_config_widget.cpp
+++ b/krita/image/kis_config_widget.cpp
@@ -1,52 +1,52 @@
/*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_config_widget.h"
#include "kis_debug.h"
#include <QTimer>
KisConfigWidget::KisConfigWidget(QWidget * parent, Qt::WFlags f, int delay)
: QWidget(parent, f)
, m_delay(delay)
{
m_timer.setSingleShot(true);
connect(&m_timer, SIGNAL(timeout()), SLOT(slotConfigChanged()));
connect(this, SIGNAL(sigConfigurationItemChanged()), SLOT(kickTimer()));
}
KisConfigWidget::~KisConfigWidget()
{
}
void KisConfigWidget::slotConfigChanged()
{
emit sigConfigurationUpdated();
}
void KisConfigWidget::kickTimer()
{
m_timer.start(m_delay);
}
void KisConfigWidget::setView(KisViewManager *view)
{
if (!view) {
- qWarning() << "KisConfigWidget::setView has got view == 0. That's a bug! Please report it!";
+ warnKrita << "KisConfigWidget::setView has got view == 0. That's a bug! Please report it!";
}
}
diff --git a/krita/image/kis_convolution_worker_fft.h b/krita/image/kis_convolution_worker_fft.h
index 20ee3a8fe2b..38c203f0dc8 100644
--- a/krita/image/kis_convolution_worker_fft.h
+++ b/krita/image/kis_convolution_worker_fft.h
@@ -1,510 +1,510 @@
/*
* Copyright (c) 2010 Edward Apap <schumifer@hotmail.com>
* Copyright (c) 2011 José Luis Vergara Toloza <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_CONVOLUTION_WORKER_FFT_H
#define KIS_CONVOLUTION_WORKER_FFT_H
#include <iostream>
#include <KoChannelInfo.h>
#include "kis_convolution_worker.h"
#include "kis_math_toolbox.h"
#include <QMutex>
#include <QVector>
#include <QTextStream>
#include <QFile>
#include <QDir>
#include <fftw3.h>
template<class _IteratorFactory_> class KisConvolutionWorkerFFT;
class KisConvolutionWorkerFFTLock
{
private:
static QMutex fftwMutex;
template<class _IteratorFactory_> friend class KisConvolutionWorkerFFT;
};
QMutex KisConvolutionWorkerFFTLock::fftwMutex;
template<class _IteratorFactory_>
class KisConvolutionWorkerFFT : public KisConvolutionWorker<_IteratorFactory_>
{
public:
KisConvolutionWorkerFFT(KisPainter *painter, KoUpdater *progress)
: KisConvolutionWorker<_IteratorFactory_>(painter, progress),
m_currentProgress(0),
m_kernelFFT(0)
{
}
~KisConvolutionWorkerFFT()
{
}
virtual void execute(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, const QRect& dataRect)
{
// Make the area we cover as small as possible
if (this->m_painter->selection())
{
QRect r = this->m_painter->selection()->selectedRect().intersect(QRect(srcPos, areaSize));
dstPos += r.topLeft() - srcPos;
srcPos = r.topLeft();
areaSize = r.size();
}
if (areaSize.width() == 0 || areaSize.height() == 0)
return;
addToProgress(0);
if (isInterrupted()) return;
const quint32 halfKernelWidth = (kernel->width() - 1) / 2;
const quint32 halfKernelHeight = (kernel->height() - 1) / 2;
m_fftWidth = areaSize.width() + 4 * halfKernelWidth;
m_fftHeight = areaSize.height() + 2 * halfKernelHeight;
/**
* FIXME: check whether this "optimization" is needed to
* be uncommented. My tests showed about 30% better performance
* when the line is commented out (DK).
*/
//optimumDimensions(m_fftWidth, m_fftHeight);
m_fftLength = m_fftHeight * (m_fftWidth / 2 + 1);
m_extraMem = (m_fftWidth % 2) ? 1 : 2;
// create and fill kernel
m_kernelFFT = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength);
memset(m_kernelFFT, 0, sizeof(fftw_complex) * m_fftLength);
fftFillKernelMatrix(kernel, m_kernelFFT);
// find out which channels need convolving
QList<KoChannelInfo*> convChannelList = this->convolvableChannelList(src);
m_channelFFT.resize(convChannelList.count());
for (quint32 i = 0; i < m_channelFFT.size(); ++i) {
m_channelFFT[i] = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * m_fftLength);
}
const double kernelFactor = kernel->factor() ? kernel->factor() : 1;
const double fftScale = 1.0 / (m_fftHeight * m_fftWidth) / kernelFactor;
FFTInfo info (fftScale, convChannelList, kernel, this->m_painter->device()->colorSpace());
int cacheRowStride = m_fftWidth + m_extraMem;
fillCacheFromDevice(src,
QRect(srcPos.x() - halfKernelWidth,
srcPos.y() - halfKernelHeight,
m_fftWidth,
m_fftHeight),
cacheRowStride,
info, dataRect);
addToProgress(10);
if (isInterrupted()) return;
// calculate number off fft operations required for progress reporting
const float progressPerFFT = (100 - 30) / (double)(convChannelList.count() * 2 + 1);
// perform FFT
fftw_plan fftwPlanForward, fftwPlanBackward;
KisConvolutionWorkerFFTLock::fftwMutex.lock();
fftwPlanForward = fftw_plan_dft_r2c_2d(m_fftHeight, m_fftWidth, (double*)m_kernelFFT, m_kernelFFT, FFTW_ESTIMATE);
fftwPlanBackward = fftw_plan_dft_c2r_2d(m_fftHeight, m_fftWidth, m_kernelFFT, (double*)m_kernelFFT, FFTW_ESTIMATE);
KisConvolutionWorkerFFTLock::fftwMutex.unlock();
fftw_execute(fftwPlanForward);
addToProgress(progressPerFFT);
if (isInterrupted()) return;
for (quint32 k = 0; k < m_channelFFT.size(); ++k)
{
fftw_execute_dft_r2c(fftwPlanForward, (double*)(m_channelFFT[k]), m_channelFFT[k]);
addToProgress(progressPerFFT);
if (isInterrupted()) return;
fftMultiply(m_channelFFT[k], m_kernelFFT);
fftw_execute_dft_c2r(fftwPlanBackward, m_channelFFT[k], (double*)m_channelFFT[k]);
addToProgress(progressPerFFT);
if (isInterrupted()) return;
}
KisConvolutionWorkerFFTLock::fftwMutex.lock();
fftw_destroy_plan(fftwPlanForward);
fftw_destroy_plan(fftwPlanBackward);
KisConvolutionWorkerFFTLock::fftwMutex.unlock();
writeResultToDevice(QRect(dstPos.x(), dstPos.y(), areaSize.width(), areaSize.height()),
cacheRowStride, halfKernelWidth, halfKernelHeight,
info, dataRect);
addToProgress(20);
cleanUp();
}
struct FFTInfo {
FFTInfo(qreal _fftScale,
QList<KoChannelInfo*> _convChannelList,
const KisConvolutionKernelSP kernel,
const KoColorSpace *colorSpace)
: fftScale(_fftScale),
convChannelList(_convChannelList),
alphaCachePos(-1),
alphaRealPos(-1)
{
KisMathToolbox* mathToolbox = KisMathToolboxRegistry::instance()->value(colorSpace->mathToolboxId().id());
for (int i = 0; i < convChannelList.count(); ++i) {
minClamp.append(mathToolbox->minChannelValue(convChannelList[i]));
maxClamp.append(mathToolbox->maxChannelValue(convChannelList[i]));
absoluteOffset.append((maxClamp[i] - minClamp[i]) * kernel->offset());
if (convChannelList[i]->channelType() == KoChannelInfo::ALPHA) {
alphaCachePos = i;
alphaRealPos = convChannelList[i]->pos();
}
}
toDoubleFuncPtr.resize(convChannelList.count());
fromDoubleFuncPtr.resize(convChannelList.count());
bool result = mathToolbox->getToDoubleChannelPtr(convChannelList, toDoubleFuncPtr);
result &= mathToolbox->getFromDoubleChannelPtr(convChannelList, fromDoubleFuncPtr);
KIS_ASSERT(result);
}
inline int numChannels() const {
return convChannelList.size();
}
QVector<qreal> minClamp;
QVector<qreal> maxClamp;
QVector<qreal> absoluteOffset;
qreal fftScale;
QList<KoChannelInfo*> convChannelList;
QVector<PtrToDouble> toDoubleFuncPtr;
QVector<PtrFromDouble> fromDoubleFuncPtr;
int alphaCachePos;
int alphaRealPos;
};
void fillCacheFromDevice(KisPaintDeviceSP src,
const QRect &rect,
const int cacheRowStride,
const FFTInfo &info,
const QRect &dataRect) {
typename _IteratorFactory_::HLineConstIterator hitSrc =
_IteratorFactory_::createHLineConstIterator(src,
rect.x(), rect.y(), rect.width(),
dataRect);
QVector<double*> channelPtr(info.numChannels());
for (quint32 k = 0; k < channelPtr.size(); ++k) {
channelPtr[k] = (double*)m_channelFFT[k];
}
for (quint32 y = 0; y < rect.height(); ++y) {
QVector<double*> cacheRowStart(channelPtr);
for (quint32 x = 0; x < rect.width(); ++x) {
const quint8 *data = hitSrc->oldRawData();
// no alpha is a rare case, so just multiply by 1.0 in that case
double alphaValue = info.alphaRealPos >= 0 ?
info.toDoubleFuncPtr[info.alphaCachePos](data, info.alphaRealPos) : 1.0;
for (quint32 k = 0; k < channelPtr.size(); ++k) {
if (k != info.alphaCachePos) {
const quint32 channelPos = info.convChannelList[k]->pos();
*channelPtr[k] = info.toDoubleFuncPtr[k](data, channelPos) * alphaValue;
} else {
*channelPtr[k] = alphaValue;
}
channelPtr[k]++;
}
hitSrc->nextPixel();
}
for (quint32 k = 0; k < channelPtr.size(); ++k) {
channelPtr[k] = cacheRowStart[k] + cacheRowStride;
}
hitSrc->nextRow();
}
}
inline void limitValue(qreal *value, qreal lowBound, qreal highBound) {
if (*value > highBound) {
*value = highBound;
} else if (*value < lowBound){
*value = lowBound;
}
}
template <bool additionalMultiplierActive>
inline qreal writeOneChannelFromCache(quint8* dstPtr,
const quint32 channel,
const int channelPos,
const FFTInfo &info,
const QVector<double*> &channelPtr,
const qreal additionalMultiplier = 0.0) {
qreal channelPixelValue;
if (additionalMultiplierActive) {
channelPixelValue = (*(channelPtr[channel]) * info.fftScale + info.absoluteOffset[channel]) * additionalMultiplier;
} else {
channelPixelValue = *(channelPtr[channel]) * info.fftScale + info.absoluteOffset[channel];
}
limitValue(&channelPixelValue, info.minClamp[channel], info.maxClamp[channel]);
info.fromDoubleFuncPtr[channel](dstPtr, channelPos, channelPixelValue);
return channelPixelValue;
}
void writeResultToDevice(const QRect &rect,
const int cacheRowStride,
const int halfKernelWidth,
const int halfKernelHeight,
const FFTInfo &info,
const QRect &dataRect) {
typename _IteratorFactory_::HLineIterator hitDst =
_IteratorFactory_::createHLineIterator(this->m_painter->device(),
rect.x(), rect.y(), rect.width(),
dataRect);
int initialOffset = cacheRowStride * halfKernelHeight + halfKernelWidth;
QVector<double*> channelPtr(info.numChannels());
for (quint32 k = 0; k < channelPtr.size(); ++k) {
channelPtr[k] = (double*)m_channelFFT[k] + initialOffset;
}
for (quint32 y = 0; y < rect.height(); ++y) {
QVector<double*> cacheRowStart(channelPtr);
for (quint32 x = 0; x < rect.width(); ++x) {
quint8 *dstPtr = hitDst->rawData();
if (info.alphaCachePos >= 0) {
qreal alphaValue =
writeOneChannelFromCache<false>(dstPtr,
info.alphaCachePos,
info.convChannelList[info.alphaCachePos]->pos(),
info,
channelPtr);
qreal alphaValueInv = 1.0 / alphaValue;
for (quint32 k = 0; k < channelPtr.size(); ++k) {
if (k != info.alphaCachePos) {
writeOneChannelFromCache<true>(dstPtr,
k,
info.convChannelList[k]->pos(),
info,
channelPtr,
alphaValueInv);
}
++channelPtr[k];
}
} else {
for (quint32 k = 0; k < channelPtr.size(); ++k) {
writeOneChannelFromCache<false>(dstPtr,
k,
info.convChannelList[k]->pos(),
info,
channelPtr);
++channelPtr[k];
}
}
hitDst->nextPixel();
}
for (quint32 k = 0; k < channelPtr.size(); ++k) {
channelPtr[k] = cacheRowStart[k] + cacheRowStride;
}
hitDst->nextRow();
}
}
private:
void fftFillKernelMatrix(const KisConvolutionKernelSP kernel, fftw_complex *m_kernelFFT)
{
// find central item
QPoint offset((kernel->width() - 1) / 2, (kernel->height() - 1) / 2);
qint32 xShift = m_fftWidth - offset.x();
qint32 yShift = m_fftHeight - offset.y();
quint32 absXpos, absYpos;
for (quint32 y = 0; y < kernel->height(); y++)
{
absYpos = y + yShift;
if (absYpos >= m_fftHeight)
absYpos -= m_fftHeight;
for (quint32 x = 0; x < kernel->width(); x++)
{
absXpos = x + xShift;
if (absXpos >= m_fftWidth)
absXpos -= m_fftWidth;
((double*)m_kernelFFT)[(m_fftWidth + m_extraMem) * absYpos + absXpos] = kernel->data()->coeff(y, x);
}
}
}
void fftMultiply(fftw_complex* channel, fftw_complex* kernel)
{
// perform complex multiplication
fftw_complex *channelPtr = channel;
fftw_complex *kernelPtr = kernel;
fftw_complex tmp;
for (quint32 pixelPos = 0; pixelPos < m_fftLength; ++pixelPos)
{
tmp[0] = ((*channelPtr)[0] * (*kernelPtr)[0]) - ((*channelPtr)[1] * (*kernelPtr)[1]);
tmp[1] = ((*channelPtr)[0] * (*kernelPtr)[1]) + ((*channelPtr)[1] * (*kernelPtr)[0]);
(*channelPtr)[0] = tmp[0];
(*channelPtr)[1] = tmp[1];
++channelPtr;
++kernelPtr;
}
}
void optimumDimensions(quint32& w, quint32& h)
{
// FFTW is most efficient when array size is a factor of 2, 3, 5 or 7
quint32 optW = w, optH = h;
while ((optW % 2 != 0) || (optW % 3 != 0) || (optW % 5 != 0) || (optW % 7 != 0))
++optW;
while ((optH % 2 != 0) || (optH % 3 != 0) || (optH % 5 != 0) || (optH % 7 != 0))
++optH;
quint32 optAreaW = optW * h;
quint32 optAreaH = optH * w;
if (optAreaW < optAreaH) {
w = optW;
}
else {
h = optH;
}
}
void fftLogMatrix(double* channel, QString f)
{
KisConvolutionWorkerFFTLock::fftwMutex.lock();
QString filename(QDir::homePath() + "/log_" + f + ".txt");
- qDebug() << "Log File Name: " << filename;
+ dbgKrita << "Log File Name: " << filename;
QFile file (filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
- qDebug() << "Failed";
+ dbgKrita << "Failed";
KisConvolutionWorkerFFTLock::fftwMutex.unlock();
return;
}
QTextStream in(&file);
for (quint32 y = 0; y < m_fftHeight; y++)
{
for (quint32 x = 0; x < m_fftWidth; x++)
{
QString num = QString::number(channel[y * m_fftWidth + x]);
while (num.length() < 15)
num += " ";
in << num << " ";
}
in << "\n";
}
KisConvolutionWorkerFFTLock::fftwMutex.unlock();
}
void addToProgress(float amount)
{
m_currentProgress += amount;
if (this->m_progress) {
this->m_progress->setProgress((int)m_currentProgress);
}
}
bool isInterrupted()
{
if (this->m_progress && this->m_progress->interrupted()) {
cleanUp();
return true;
}
return false;
}
void cleanUp()
{
// free kernel fft data
if (m_kernelFFT) {
fftw_free(m_kernelFFT);
}
foreach (fftw_complex *channel, m_channelFFT) {
fftw_free(channel);
}
m_channelFFT.clear();
}
private:
quint32 m_fftWidth, m_fftHeight, m_fftLength, m_extraMem;
float m_currentProgress;
fftw_complex* m_kernelFFT;
QVector<fftw_complex*> m_channelFFT;
};
#endif
diff --git a/krita/image/kis_debug.cpp b/krita/image/kis_debug.cpp
deleted file mode 100644
index cb70be93197..00000000000
--- a/krita/image/kis_debug.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "kis_debug.h"
-
-#include <string>
-
-#include <QRect>
-#include <QString>
-#include <QImage>
-
-#include "kis_paint_device.h"
-
-
-void kis_debug_save_device_incremental(KisPaintDeviceSP device,
- int i,
- const QRect &rc,
- const QString &suffix, const QString &prefix)
-{
- QString filename = QString("%1_%2.png").arg(i).arg(suffix);
-
- if (!prefix.isEmpty()) {
- filename = QString("%1_%2.png").arg(prefix).arg(filename);
- }
-
- QRect saveRect(rc);
-
- if (saveRect.isEmpty()) {
- saveRect = device->exactBounds();
- }
-
- qDebug() << "Dumping:" << filename;
- device->convertToQImage(0, saveRect).save(filename);
-}
-
-const char* __methodName(const char *_prettyFunction)
-{
- std::string prettyFunction(_prettyFunction);
-
- size_t colons = prettyFunction.find("::");
- size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
- size_t end = prettyFunction.rfind("(") - begin;
-
- return std::string(prettyFunction.substr(begin,end) + "()").c_str();
-}
diff --git a/krita/image/kis_debug.h b/krita/image/kis_debug.h
deleted file mode 100644
index e4041f47d60..00000000000
--- a/krita/image/kis_debug.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-#ifndef KIS_DEBUG_AREAS_H_
-#define KIS_DEBUG_AREAS_H_
-
-#include <kdebug.h>
-
-#define dbgResources kDebug(30009)
-#define dbgKrita kDebug(41000) // For temporary debug lines, where you'd have used kDebug() before.
-#define dbgImage kDebug(41001)
-#define dbgRegistry kDebug(41002)
-#define dbgTools kDebug(41003)
-#define dbgTiles kDebug(41004)
-#define dbgFilters kDebug(41005)
-#define dbgPlugins kDebug(41006)
-#define dbgUI kDebug(41007)
-#define dbgFile kDebug(41008)
-#define dbgMath kDebug(41009)
-#define dbgRender kDebug(41010)
-#define dbgScript kDebug(41011)
-
-#define warnKrita kWarning(41000) // For temporary debug lines, where you'd have used kWarning() before.
-#define warnImage kWarning(41001)
-#define warnRegistry kWarning(41002)
-#define warnTools kWarning(41003)
-#define warnTiles kWarning(41004)
-#define warnFilters kWarning(41005)
-#define warnPlugins kWarning(41006)
-#define warnUI kWarning(41007)
-#define warnFile kWarning(41008)
-#define warnMath kWarning(41009)
-#define warnRender kWarning(41010)
-#define warnScript kWarning(41011)
-
-#define errKrita kError(41000) // For temporary debug lines, where you'd have used kError() before.
-#define errImage kError(41001)
-#define errRegistry kError(41002)
-#define errTools kError(41003)
-#define errTiles kError(41004)
-#define errFilters kError(41005)
-#define errPlugins kError(41006)
-#define errUI kError(41007)
-#define errFile kError(41008)
-#define errMath kError(41009)
-#define errRender kError(41010)
-#define errScript kError(41011)
-
-#define fatalKrita kFatal(41000) // For temporary debug lines, where you'd have used kFatal() before.
-#define fatalImage kFatal(41001)
-#define fatalRegistry kFatal(41002)
-#define fatalTools kFatal(41003)
-#define fatalTiles kFatal(41004)
-#define fatalFilters kFatal(41005)
-#define fatalPlugins kFatal(41006)
-#define fatalUI kFatal(41007)
-#define fatalFile kFatal(41008)
-#define fatalMath kFatal(41009)
-#define fatalRender kFatal(41010)
-#define fatalScript kFatal(41011)
-#endif
-
-/**
- * Use this macro to display in the output stream the name of a variable followed by its value.
- */
-#define ppVar( var ) #var << "=" << var
-
-#ifdef __GNUC__
-#define ENTER_FUNCTION() qDebug() << "Entering" << __func__
-#define LEAVE_FUNCTION() qDebug() << "Leaving " << __func__
-#else
-#define ENTER_FUNCTION() qDebug() << "Entering" << "<unknown>"
-#define LEAVE_FUNCTION() qDebug() << "Leaving " << "<unknown>"
-#endif
-
-# ifndef QT_NO_DEBUG
-# undef Q_ASSERT
-# define Q_ASSERT(cond) if(!(cond)) { kError() << kBacktrace(); qt_assert(#cond,__FILE__,__LINE__); } qt_noop()
-# endif
-
-
-#include "kritaimage_export.h"
-#include "kis_types.h"
-class QRect;
-class QString;
-
-void KRITAIMAGE_EXPORT kis_debug_save_device_incremental(KisPaintDeviceSP device,
- int i,
- const QRect &rc,
- const QString &suffix, const QString &prefix);
-
-/**
- * Saves the paint device incrementally. Put this macro into a
- * function that is called several times and you'll have as many
- * separate dump files as the number of times the function was
- * called. That is very convenient for debugging canvas updates:
- * adding this macro will let you track the whole history of updates.
- *
- * The files are saved with pattern: <counter>_<suffix>.png
- */
-#define KIS_DUMP_DEVICE_1(device, rc, suffix) \
- do { \
- static int i = -1; i++; \
- kis_debug_save_device_incremental((device), i, (rc), (suffix), QString()); \
- } while(0)
-
-/**
- * Saves the paint device incrementally. Put this macro into a
- * function that is called several times and you'll have as many
- * separate dump files as the number of times the function was
- * called. That is very convenient for debugging canvas updates:
- * adding this macro will let you track the whole history of updates.
- *
- * The \p prefix parameter makes it easy to sort out dumps from
- * different functions.
- *
- * The files are saved with pattern: <prefix>_<counter>_<suffix>.png
- */
-#define KIS_DUMP_DEVICE_2(device, rc, suffix, prefix) \
- do { \
- static int i = -1; i++; \
- kis_debug_save_device_incremental((device), i, (rc), (suffix), (prefix)); \
- } while(0)
-
-
-
-#ifdef __GNUC__
-KRITAIMAGE_EXPORT const char* __methodName(const char *prettyFunction);
-#define __METHOD_NAME__ __methodName(__PRETTY_FUNCTION__)
-#else
-#define __METHOD_NAME__ "<unknown>:<unknown>"
-#endif
-
-#define PREPEND_METHOD(msg) QString("%1: %2").arg(__METHOD_NAME__).arg(msg)
-
-#include "kis_assert.h"
diff --git a/krita/image/kis_distance_information.cpp b/krita/image/kis_distance_information.cpp
index 29a5a3097a8..d344739a6cf 100644
--- a/krita/image/kis_distance_information.cpp
+++ b/krita/image/kis_distance_information.cpp
@@ -1,211 +1,211 @@
/*
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_distance_information.h"
#include "kis_paint_information.h"
#include "kis_debug.h"
#include <QtCore/qmath.h>
#include <QVector2D>
#include <QTransform>
#include "kis_algebra_2d.h"
struct Q_DECL_HIDDEN KisDistanceInformation::Private {
Private() : lastDabInfoValid(false),
lastPaintInfoValid(false) {}
QPointF distance;
KisSpacingInformation spacing;
QPointF lastPosition;
qreal lastTime;
bool lastDabInfoValid;
KisPaintInformation lastPaintInformation;
qreal lastAngle;
bool lastPaintInfoValid;
};
KisDistanceInformation::KisDistanceInformation()
: m_d(new Private)
{
}
KisDistanceInformation::KisDistanceInformation(const QPointF &lastPosition,
qreal lastTime)
: m_d(new Private)
{
m_d->lastPosition = lastPosition;
m_d->lastTime = lastTime;
m_d->lastDabInfoValid = true;
}
KisDistanceInformation::KisDistanceInformation(const KisDistanceInformation &rhs)
: m_d(new Private(*rhs.m_d))
{
}
KisDistanceInformation& KisDistanceInformation::operator=(const KisDistanceInformation &rhs)
{
*m_d = *rhs.m_d;
return *this;
}
KisDistanceInformation::~KisDistanceInformation()
{
delete m_d;
}
const KisSpacingInformation& KisDistanceInformation::currentSpacing() const
{
return m_d->spacing;
}
bool KisDistanceInformation::hasLastDabInformation() const
{
return m_d->lastDabInfoValid;
}
QPointF KisDistanceInformation::lastPosition() const
{
return m_d->lastPosition;
}
qreal KisDistanceInformation::lastTime() const
{
return m_d->lastTime;
}
qreal KisDistanceInformation::lastDrawingAngle() const
{
return m_d->lastAngle;
}
bool KisDistanceInformation::hasLastPaintInformation() const
{
return m_d->lastPaintInfoValid;
}
const KisPaintInformation& KisDistanceInformation::lastPaintInformation() const
{
return m_d->lastPaintInformation;
}
void KisDistanceInformation::registerPaintedDab(const KisPaintInformation &info,
const KisSpacingInformation &spacing)
{
m_d->lastAngle = info.drawingAngleSafe(*this);
m_d->lastPaintInformation = info;
m_d->lastPaintInfoValid = true;
m_d->lastPosition = info.pos();
m_d->lastTime = info.currentTime();
m_d->lastDabInfoValid = true;
m_d->spacing = spacing;
}
qreal KisDistanceInformation::getNextPointPosition(const QPointF &start,
const QPointF &end)
{
return m_d->spacing.isIsotropic() ?
getNextPointPositionIsotropic(start, end) :
getNextPointPositionAnisotropic(start, end);
}
qreal KisDistanceInformation::getNextPointPositionIsotropic(const QPointF &start,
const QPointF &end)
{
qreal distance = m_d->distance.x();
qreal spacing = qMax(qreal(0.5), m_d->spacing.spacing().x());
if (start == end) {
return -1;
}
qreal dragVecLength = QVector2D(end - start).length();
qreal nextPointDistance = spacing - distance;
qreal t;
if (nextPointDistance <= dragVecLength) {
t = nextPointDistance / dragVecLength;
m_d->distance = QPointF();
} else {
t = -1;
m_d->distance.rx() += dragVecLength;
}
return t;
}
qreal KisDistanceInformation::getNextPointPositionAnisotropic(const QPointF &start,
const QPointF &end)
{
if (start == end) {
return -1;
}
qreal a_rev = 1.0 / qMax(qreal(0.5), m_d->spacing.spacing().x());
qreal b_rev = 1.0 / qMax(qreal(0.5), m_d->spacing.spacing().y());
qreal x = m_d->distance.x();
qreal y = m_d->distance.y();
static const qreal eps = 2e-3; // < 0.2 deg
const qreal currentRotation = m_d->spacing.rotation();
QPointF diff = end - start;
if (currentRotation > eps) {
QTransform rot;
// since the ellipse is symmetrical, the sign
// of rotation doesn't matter
rot.rotateRadians(currentRotation);
diff = rot.map(diff);
}
qreal dx = qAbs(diff.x());
qreal dy = qAbs(diff.y());
qreal alpha = pow2(dx * a_rev) + pow2(dy * b_rev);
qreal beta = x * dx * a_rev * a_rev + y * dy * b_rev * b_rev;
qreal gamma = pow2(x * a_rev) + pow2(y * b_rev) - 1;
qreal D_4 = pow2(beta) - alpha * gamma;
qreal t = -1.0;
if (D_4 >= 0) {
qreal k = (-beta + qSqrt(D_4)) / alpha;
if (k >= 0.0 && k <= 1.0) {
t = k;
m_d->distance = QPointF();
} else {
m_d->distance += KisAlgebra2D::abs(diff);
}
} else {
- qWarning() << "BUG: No solution for elliptical spacing equation has been found. This shouldn't have happened.";
+ warnKrita << "BUG: No solution for elliptical spacing equation has been found. This shouldn't have happened.";
}
return t;
}
diff --git a/krita/image/kis_dom_utils.cpp b/krita/image/kis_dom_utils.cpp
index 88d7724dd20..3f3dfe70486 100644
--- a/krita/image/kis_dom_utils.cpp
+++ b/krita/image/kis_dom_utils.cpp
@@ -1,234 +1,234 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_dom_utils.h"
#include <QTransform>
-#include <QDebug>
+#include <kis_debug.h>
#include "kis_debug.h"
namespace KisDomUtils {
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "size");
e.setAttribute("w", Private::numberToString(size.width()));
e.setAttribute("h", Private::numberToString(size.height()));
}
void saveValue(QDomElement *parent, const QString &tag, const QRect &rc)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "rect");
e.setAttribute("x", Private::numberToString(rc.x()));
e.setAttribute("y", Private::numberToString(rc.y()));
e.setAttribute("w", Private::numberToString(rc.width()));
e.setAttribute("h", Private::numberToString(rc.height()));
}
void saveValue(QDomElement *parent, const QString &tag, const QPointF &pt)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "pointf");
e.setAttribute("x", Private::numberToString(pt.x()));
e.setAttribute("y", Private::numberToString(pt.y()));
}
void saveValue(QDomElement *parent, const QString &tag, const QVector3D &pt)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "vector3d");
e.setAttribute("x", Private::numberToString(pt.x()));
e.setAttribute("y", Private::numberToString(pt.y()));
e.setAttribute("z", Private::numberToString(pt.z()));
}
void saveValue(QDomElement *parent, const QString &tag, const QTransform &t)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "transform");
e.setAttribute("m11", Private::numberToString(t.m11()));
e.setAttribute("m12", Private::numberToString(t.m12()));
e.setAttribute("m13", Private::numberToString(t.m13()));
e.setAttribute("m21", Private::numberToString(t.m21()));
e.setAttribute("m22", Private::numberToString(t.m22()));
e.setAttribute("m23", Private::numberToString(t.m23()));
e.setAttribute("m31", Private::numberToString(t.m31()));
e.setAttribute("m32", Private::numberToString(t.m32()));
e.setAttribute("m33", Private::numberToString(t.m33()));
}
bool findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages)
{
QDomNodeList list = parent.elementsByTagName(tag);
if (list.size() != 1 || !list.at(0).isElement()) {
QString msg = i18n("Could not find \"%1\" XML tag in \"%2\"", tag, parent.tagName());
if (errorMessages) {
*errorMessages << msg;
} else {
- qWarning() << msg;
+ warnKrita << msg;
}
return false;
}
*el = list.at(0).toElement();
return true;
}
namespace Private {
bool checkType(const QDomElement &e, const QString &expectedType)
{
QString type = e.attribute("type", "unknown-type");
if (type != expectedType) {
- qWarning() << i18n("Error: incorrect type (%2) for value %1. Expected %3", e.tagName(), type, expectedType);
+ warnKrita << i18n("Error: incorrect type (%2) for value %1. Expected %3", e.tagName(), type, expectedType);
return false;
}
return true;
}
}
bool loadValue(const QDomElement &e, float *v)
{
if (!Private::checkType(e, "value")) return false;
*v = Private::stringToDouble(e.attribute("value", "0"));
return true;
}
bool loadValue(const QDomElement &e, double *v)
{
if (!Private::checkType(e, "value")) return false;
*v = Private::stringToDouble(e.attribute("value", "0"));
return true;
}
bool loadValue(const QDomElement &e, QSize *size)
{
if (!Private::checkType(e, "size")) return false;
size->setWidth(Private::stringToInt(e.attribute("w", "0")));
size->setHeight(Private::stringToInt(e.attribute("h", "0")));
return true;
}
bool loadValue(const QDomElement &e, QRect *rc)
{
if (!Private::checkType(e, "rect")) return false;
rc->setX(Private::stringToInt(e.attribute("x", "0")));
rc->setY(Private::stringToInt(e.attribute("y", "0")));
rc->setWidth(Private::stringToInt(e.attribute("w", "0")));
rc->setHeight(Private::stringToInt(e.attribute("h", "0")));
return true;
}
bool loadValue(const QDomElement &e, QPointF *pt)
{
if (!Private::checkType(e, "pointf")) return false;
pt->setX(Private::stringToDouble(e.attribute("x", "0")));
pt->setY(Private::stringToDouble(e.attribute("y", "0")));
return true;
}
bool loadValue(const QDomElement &e, QVector3D *pt)
{
if (!Private::checkType(e, "vector3d")) return false;
pt->setX(Private::stringToDouble(e.attribute("x", "0")));
pt->setY(Private::stringToDouble(e.attribute("y", "0")));
pt->setZ(Private::stringToDouble(e.attribute("z", "0")));
return true;
}
bool loadValue(const QDomElement &e, QTransform *t)
{
if (!Private::checkType(e, "transform")) return false;
qreal m11 = Private::stringToDouble(e.attribute("m11", "1.0"));
qreal m12 = Private::stringToDouble(e.attribute("m12", "0.0"));
qreal m13 = Private::stringToDouble(e.attribute("m13", "0.0"));
qreal m21 = Private::stringToDouble(e.attribute("m21", "0.0"));
qreal m22 = Private::stringToDouble(e.attribute("m22", "1.0"));
qreal m23 = Private::stringToDouble(e.attribute("m23", "0.0"));
qreal m31 = Private::stringToDouble(e.attribute("m31", "0.0"));
qreal m32 = Private::stringToDouble(e.attribute("m32", "0.0"));
qreal m33 = Private::stringToDouble(e.attribute("m33", "1.0"));
t->setMatrix(
m11, m12, m13,
m21, m22, m23,
m31, m32, m33);
return true;
}
QDomElement findElementByAttibute(QDomNode parent,
const QString &tag,
const QString &attribute,
const QString &key)
{
QDomNode node = parent.firstChild();
while (!node.isNull()) {
QDomElement el = node.toElement();
if (!el.isNull() && el.tagName() == tag) {
QString value = el.attribute(attribute, "<undefined>");
if (value == key) return el;
}
node = node.nextSibling();
}
return QDomElement();
}
}
diff --git a/krita/image/kis_dom_utils.h b/krita/image/kis_dom_utils.h
index 4059224a463..66a9c011e20 100644
--- a/krita/image/kis_dom_utils.h
+++ b/krita/image/kis_dom_utils.h
@@ -1,235 +1,235 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_DOM_UTILS_H
#define __KIS_DOM_UTILS_H
#include <QPointF>
#include <QVector3D>
#include <QVector>
#include <QDomElement>
#include <QLocale>
#include "klocale.h"
#include "kritaimage_export.h"
#include "kis_debug.h"
namespace KisDomUtils {
namespace Private {
inline QString numberToString(const QString &value) {
return value;
}
template<typename T>
inline QString numberToString(T value) {
return QString::number(value);
}
inline int stringToInt(const QString &str) {
bool ok = false;
int value = 0;
QLocale c(QLocale::German);
value = str.toInt(&ok);
if (!ok) {
value = c.toInt(str, &ok);
}
if (!ok) {
- qWarning() << "WARNING: KisDomUtils::stringToInt failed:" << ppVar(str);
+ warnKrita << "WARNING: KisDomUtils::stringToInt failed:" << ppVar(str);
value = 0;
}
return value;
}
inline double stringToDouble(const QString &str) {
bool ok = false;
double value = 0;
QLocale c(QLocale::German);
/**
* A special workaround to handle ','/'.' decimal point
* in different locales. Added for backward compatibility,
* because we used to save qreals directly using
*
* e.setAttribute("w", (qreal)value),
*
* which did local-aware conversion.
*/
value = str.toDouble(&ok);
if (!ok) {
value = c.toDouble(str, &ok);
}
if (!ok) {
- qWarning() << "WARNING: KisDomUtils::stringToDouble failed:" << ppVar(str);
+ warnKrita << "WARNING: KisDomUtils::stringToDouble failed:" << ppVar(str);
value = 0;
}
return value;
}
}
/**
* Save a value of type QRect into an XML tree. A child for \p parent
* is created and assigned a tag \p tag. The corresponding value can
* be fetched from the XML using loadValue() later.
*
* \see loadValue()
*/
void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const QRect &rc);
void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const QSize &size);
void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const QPointF &pt);
void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const QVector3D &pt);
void KRITAIMAGE_EXPORT saveValue(QDomElement *parent, const QString &tag, const QTransform &t);
/**
* Save a value of a scalar type into an XML tree. A child for \p parent
* is created and assigned a tag \p tag. The corresponding value can
* be fetched from the XML using loadValue() later.
*
* \see loadValue()
*/
template <typename T>
void saveValue(QDomElement *parent, const QString &tag, T value)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "value");
e.setAttribute("value", Private::numberToString(value));
}
/**
* Save a vector of values into an XML tree. A child for \p parent is
* created and assigned a tag \p tag. The values in the array should
* have a type supported by saveValue() overrides. The corresponding
* vector can be fetched from the XML using loadValue() later.
*
* \see loadValue()
*/
template <typename T>
void saveValue(QDomElement *parent, const QString &tag, const QVector<T> &array)
{
QDomDocument doc = parent->ownerDocument();
QDomElement e = doc.createElement(tag);
parent->appendChild(e);
e.setAttribute("type", "array");
int i = 0;
foreach (const T &v, array) {
saveValue(&e, QString("item_%1").arg(i++), v);
}
}
/**
* Find an element with tag \p tag which is a child of \p parent. The element should
* be the only element with the provided tag in this parent.
*
* \return true is the element with \p tag is found and it is unique
*/
bool KRITAIMAGE_EXPORT findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages = 0);
/**
* Load an object from an XML element, which is a child of \p parent and has
* a tag \p tag.
*
* \return true if the object is successfully loaded and is unique
*
* \see saveValue()
*/
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, float *v);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, double *v);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, QSize *size);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, QRect *rc);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, QPointF *pt);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, QVector3D *pt);
bool KRITAIMAGE_EXPORT loadValue(const QDomElement &e, QTransform *t);
namespace Private {
bool KRITAIMAGE_EXPORT checkType(const QDomElement &e, const QString &expectedType);
}
/**
* Load a scalar value from an XML element, which is a child of \p parent
* and has a tag \p tag.
*
* \return true if the object is successfully loaded and is unique
*
* \see saveValue()
*/
template <typename T>
bool loadValue(const QDomElement &e, T *value)
{
if (!Private::checkType(e, "value")) return false;
QVariant v(e.attribute("value", "no-value"));
*value = v.value<T>();
return true;
}
/**
* Load an array from an XML element, which is a child of \p parent
* and has a tag \p tag.
*
* \return true if the object is successfully loaded and is unique
*
* \see saveValue()
*/
template <typename T>
bool loadValue(const QDomElement &e, QVector<T> *array)
{
if (!Private::checkType(e, "array")) return false;
QDomElement child = e.firstChildElement();
while (!child.isNull()) {
T value;
if (!loadValue(child, &value)) return false;
*array << value;
child = child.nextSiblingElement();
}
return true;
}
template <typename T>
bool loadValue(const QDomElement &parent, const QString &tag, T *value)
{
QDomElement e;
if (!findOnlyElement(parent, tag, &e)) return false;
return loadValue(e, value);
}
KRITAIMAGE_EXPORT QDomElement findElementByAttibute(QDomNode parent,
const QString &tag,
const QString &attribute,
const QString &key);
}
#endif /* __KIS_DOM_UTILS_H */
diff --git a/krita/image/kis_fill_painter.cc b/krita/image/kis_fill_painter.cc
index bbaba149780..65ba2725ad7 100644
--- a/krita/image/kis_fill_painter.cc
+++ b/krita/image/kis_fill_painter.cc
@@ -1,327 +1,327 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_fill_painter.h"
#include <stdlib.h>
#include <string.h>
#include <cfloat>
#include <stack>
#include <QFontInfo>
#include <QFontMetrics>
#include <QPen>
#include <QMatrix>
#include <QImage>
#include <QMap>
#include <QPainter>
#include <QRect>
#include <QString>
#include <klocale.h>
#include <KoUpdater.h>
#include "generator/kis_generator.h"
#include "filter/kis_filter_configuration.h"
#include "generator/kis_generator_registry.h"
#include "kis_processing_information.h"
#include "kis_debug.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "KoPattern.h"
#include "KoColorSpace.h"
#include "kis_transaction.h"
#include "kis_types.h"
#include "kis_pixel_selection.h"
#include <KoCompositeOpRegistry.h>
#include <floodfill/kis_scanline_fill.h>
#include "kis_random_accessor_ng.h"
#include "KoColor.h"
#include "kis_selection.h"
#include "kis_selection_filters.h"
KisFillPainter::KisFillPainter()
: KisPainter()
{
initFillPainter();
}
KisFillPainter::KisFillPainter(KisPaintDeviceSP device)
: KisPainter(device)
{
initFillPainter();
}
KisFillPainter::KisFillPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: KisPainter(device, selection)
{
initFillPainter();
}
void KisFillPainter::initFillPainter()
{
m_width = m_height = -1;
m_careForSelection = false;
m_sizemod = 0;
m_feather = 0;
m_useCompositioning = false;
m_threshold = 0;
}
void KisFillPainter::fillSelection(const QRect &rc, const KoColor &color)
{
KisPaintDeviceSP fillDevice = new KisPaintDevice(device()->colorSpace());
fillDevice->setDefaultPixel(color.data());
bitBlt(rc.topLeft(), fillDevice, rc);
}
// 'regular' filling
// XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind,
// this is more eraseToColor.
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoColor& kc, quint8 opacity)
{
if (w > 0 && h > 0) {
// Make sure we're in the right colorspace
KoColor kc2(kc); // get rid of const
kc2.convertTo(device()->colorSpace());
quint8 * data = kc2.data();
device()->colorSpace()->setOpacity(data, opacity, 1);
device()->fill(x1, y1, w, h, data);
addDirtyRect(QRect(x1, y1, w, h));
}
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern)
{
if (!pattern) return;
if (!pattern->valid()) return;
if (!device()) return;
if (w < 1) return;
if (h < 1) return;
KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->compositionSourceColorSpace(), pattern->name());
patternLayer->convertFromQImage(pattern->pattern(), 0);
fillRect(x1, y1, w, h, patternLayer, QRect(0, 0, pattern->width(), pattern->height()));
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect)
{
Q_ASSERT(deviceRect.x() == 0); // the case x,y != 0,0 is not yet implemented
Q_ASSERT(deviceRect.y() == 0);
int sx, sy, sw, sh;
int y = y1;
if (y >= 0) {
sy = y % deviceRect.height();
} else {
sy = deviceRect.height() - (((-y - 1) % deviceRect.height()) + 1);
}
while (y < y1 + h) {
sh = qMin((y1 + h) - y, deviceRect.height() - sy);
int x = x1;
if (x >= 0) {
sx = x % deviceRect.width();
} else {
sx = deviceRect.width() - (((-x - 1) % deviceRect.width()) + 1);
}
while (x < x1 + w) {
sw = qMin((x1 + w) - x, deviceRect.width() - sx);
bitBlt(x, y, device, sx, sy, sw, sh);
x += sw; sx = 0;
}
y += sh; sy = 0;
}
addDirtyRect(QRect(x1, y1, w, h));
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisFilterConfiguration* generator)
{
if (!generator) return;
KisGeneratorSP g = KisGeneratorRegistry::instance()->value(generator->name());
if (!device()) return;
if (w < 1) return;
if (h < 1) return;
QRect tmpRc(x1, y1, w, h);
KisProcessingInformation dstCfg(device(), tmpRc.topLeft(), 0);
g->generate(dstCfg, tmpRc.size(), generator);
addDirtyRect(tmpRc);
}
// flood filling
void KisFillPainter::fillColor(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
if (!m_useCompositioning) {
if (m_sizemod || m_feather ||
compositeOp()->id() != COMPOSITE_OVER ||
opacity() != MAX_SELECTED ||
sourceDevice != device()) {
- qWarning() << "WARNING: Fast Flood Fill (no compositioning mode)"
+ warnKrita << "WARNING: Fast Flood Fill (no compositioning mode)"
<< "does not support compositeOps, opacity, "
<< "selection enhancements and separate source "
<< "devices";
}
QRect fillBoundsRect(0, 0, m_width, m_height);
QPoint startPoint(startX, startY);
if (!fillBoundsRect.contains(startPoint)) return;
KisScanlineFill gc(device(), startPoint, fillBoundsRect);
gc.setThreshold(m_threshold);
gc.fillColor(paintColor());
} else {
genericFillStart(startX, startY, sourceDevice);
// Now create a layer and fill it
KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
Q_CHECK_PTR(filled);
KisFillPainter painter(filled);
painter.fillRect(0, 0, m_width, m_height, paintColor());
painter.end();
genericFillEnd(filled);
}
}
void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
genericFillStart(startX, startY, sourceDevice);
// Now create a layer and fill it
KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
Q_CHECK_PTR(filled);
KisFillPainter painter(filled);
painter.fillRect(0, 0, m_width, m_height, pattern());
painter.end();
genericFillEnd(filled);
}
void KisFillPainter::genericFillStart(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
Q_ASSERT(m_width > 0);
Q_ASSERT(m_height > 0);
// Create a selection from the surrounding area
m_fillSelection = createFloodSelection(startX, startY, sourceDevice);
}
void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled)
{
if (progressUpdater() && progressUpdater()->interrupted()) {
m_width = m_height = -1;
return;
}
// TODO: filling using the correct bound of the selection would be better, *but*
// the selection is limited to the exact bound of a layer, while in reality, we don't
// want that, since we want a transparent layer to be completely filled
// QRect rc = m_fillSelection->selectedExactRect();
/**
* Apply the real selection to a filled one
*/
KisSelectionSP realSelection = selection();
if (realSelection) {
m_fillSelection->pixelSelection()->applySelection(
realSelection->projection(), SELECTION_INTERSECT);
}
setSelection(m_fillSelection);
bitBlt(0, 0, filled, 0, 0, m_width, m_height);
setSelection(realSelection);
if (progressUpdater()) progressUpdater()->setProgress(100);
m_width = m_height = -1;
}
KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice)
{
if (m_width < 0 || m_height < 0) {
if (selection() && m_careForSelection) {
QRect rc = selection()->selectedExactRect();
m_width = rc.width() - (startX - rc.x());
m_height = rc.height() - (startY - rc.y());
}
}
dbgImage << "Width: " << m_width << " Height: " << m_height;
// Otherwise the width and height should have been set
Q_ASSERT(m_width > 0 && m_height > 0);
QRect fillBoundsRect(0, 0, m_width, m_height);
QPoint startPoint(startX, startY);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(device()));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
if (!fillBoundsRect.contains(startPoint)) {
return selection;
}
KisScanlineFill gc(sourceDevice, startPoint, fillBoundsRect);
gc.setThreshold(m_threshold);
gc.fillSelection(pixelSelection);
if (m_sizemod > 0) {
KisGrowSelectionFilter biggy(m_sizemod, m_sizemod);
biggy.process(pixelSelection, selection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod));
}
else if (m_sizemod < 0) {
KisShrinkSelectionFilter tiny(-m_sizemod, -m_sizemod, false);
tiny.process(pixelSelection, selection->selectedRect());
}
if (m_feather > 0) {
KisFeatherSelectionFilter feathery(m_feather);
feathery.process(pixelSelection, selection->selectedRect().adjusted(-m_feather, -m_feather, m_feather, m_feather));
}
return selection;
}
diff --git a/krita/image/kis_filter_weights_applicator.h b/krita/image/kis_filter_weights_applicator.h
index 65b5001d4b7..e544162daa2 100644
--- a/krita/image/kis_filter_weights_applicator.h
+++ b/krita/image/kis_filter_weights_applicator.h
@@ -1,332 +1,333 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_FILTER_WEIGHTS_APPLICATOR_H
#define __KIS_FILTER_WEIGHTS_APPLICATOR_H
#include "kis_fixed_point_maths.h"
#include "kis_filter_weights_buffer.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "kis_paint_device.h"
#include <KoColorSpace.h>
#include <KoMixColorsOp.h>
namespace tmp {
template <class iter> iter createIterator(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len);
template <> KisHLineIteratorSP createIterator <KisHLineIteratorSP>
(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len)
{
return dev->createHLineIteratorNG(start, lineNum, len);
}
template <> KisVLineIteratorSP createIterator <KisVLineIteratorSP>
(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len)
{
return dev->createVLineIteratorNG(lineNum, start, len);
}
}
/**
* \class KisFilterWeightsApplicator
*
* This is a main class for transforming a line of pixel data. It
* transforms lines from \p src into \p dst using \p scale, \p shear
* and offset (\p dx) parameters.
*
* Notation:
* <pixel_name>_l -- leftmost border of the pixel
* <pixel_name>_c -- center of the pixel
*
*
* Example calculation of an offset (see calculateBlendSpan()):
* scale = 0.5;
* offset = <very small value>
*
* +------ dst_l
* |
* | +-- dst_c
* | |
*
* dst: | * | * | * |
*
* src: | * | * | * | * | * | * | * | * |
*
* |||
* ||+--- next_c_in_src
* |||
* |+---- dst_c_in_src
* |||
* |++--- offset (near zero, measured in dst coordinates)
* |
* +-- _l position of the pixel, which is considered
* cetral in the weights buffer
*
* Another example calculation of an offset (see calculateBlendSpan()):
* scale = 0.5;
* offset = <high value near 0.5>
*
* +------ dst_l
* |
* | +-- dst_c
* | |
*
* dst: | * | * | * |
*
* src: | * | * | * | * | * | * | * | * |
*
* || |
* || +--- next_c_in_src
* || |
* +------ dst_c_in_src
* || |
* +|-+--- offset (near 0.5, measured in dst coordinates)
* |
* +-- _l position of the pixel, which is considered
* cetral in the weights buffer
*/
class KisFilterWeightsApplicator
{
public:
KisFilterWeightsApplicator(KisPaintDeviceSP src,
KisPaintDeviceSP dst,
qreal realScale, qreal shear,
qreal dx,
bool clampToEdge)
: m_src(src),
m_dst(dst),
m_realScale(realScale),
m_shear(shear),
m_dx(dx),
m_clampToEdge(clampToEdge)
{
}
struct BlendSpan {
KisFilterWeightsBuffer::FilterWeights *weights;
int firstBlendPixel; // in src coords
KisFixedPoint offset;
KisFixedPoint offsetInc;
};
inline BlendSpan calculateBlendSpan(int dst_l, int line, KisFilterWeightsBuffer *buffer) const {
KisFixedPoint dst_c = l_to_c(dst_l);
KisFixedPoint dst_c_in_src = dstToSrc(dst_c.toFloat(), line);
KisFixedPoint next_c_in_src = (dst_c_in_src - qreal(0.5)).toIntCeil() + qreal(0.5);
BlendSpan span;
span.offset = (next_c_in_src - dst_c_in_src) * buffer->weightsPositionScale();
span.offsetInc = buffer->weightsPositionScale();
Q_ASSERT(span.offset <= span.offsetInc);
span.weights = buffer->weights(span.offset);
span.firstBlendPixel = next_c_in_src.toIntFloor() - span.weights->centerIndex;
return span;
}
class LinePos {
public:
LinePos()
: m_start(0), m_size(0)
{
}
LinePos(int start, int size)
: m_start(start), m_size(size)
{
}
inline int start() const {
return m_start;
}
/**
* WARNING: be careful! This is not the same as
* QRect::right()! This is an equivalent of (QRect::right() +
* QRect::width()) or QRectF::right(), that is it points to
* the pixel after(!) the actual last pixel. See Qt docs for
* more info about this historical difference.
*/
inline int end() const {
return m_start + m_size;
}
inline int size() const {
return m_size;
}
inline void unite(const LinePos &rhs) {
if (m_size > 0) {
int newStart = qMin(start(), rhs.start());
int newEnd = qMax(end(), rhs.end());
m_start = newStart;
m_size = newEnd - newStart;
} else {
m_start = rhs.start();
m_size = rhs.size();
}
}
private:
int m_start;
int m_size;
};
template <class T>
LinePos processLine(LinePos srcLine, int line, KisFilterWeightsBuffer *buffer, qreal filterSupport) {
int dstStart;
int dstEnd;
int leftSrcBorder;
int rightSrcBorder;
if (m_realScale >= 0) {
dstStart = findAntialiasedDstStart(srcLine.start(), filterSupport, line);
dstEnd = findAntialiasedDstEnd(srcLine.end(), filterSupport, line);
leftSrcBorder = getLeftSrcNeedBorder(dstStart, line, buffer);
rightSrcBorder = getRightSrcNeedBorder(dstEnd - 1, line, buffer);
- } else {
+ }
+ else {
dstStart = findAntialiasedDstStart(srcLine.end(), filterSupport, line);
dstEnd = findAntialiasedDstEnd(srcLine.start(), filterSupport, line);
leftSrcBorder = getLeftSrcNeedBorder(dstEnd - 1, line, buffer);
rightSrcBorder = getRightSrcNeedBorder(dstStart, line, buffer);
}
- //Q_ASSERT(dstStart < dstEnd);
- //Q_ASSERT(leftSrcBorder < rightSrcBorder);
- //Q_ASSERT(leftSrcBorder <= srcLine.start());
- //Q_ASSERT(srcLine.end() <= rightSrcBorder);
+ if (dstStart >= dstEnd) return LinePos(dstStart, 0);
+ if (leftSrcBorder >= rightSrcBorder) return LinePos(dstStart, 0);
+ if (leftSrcBorder > srcLine.start()) return LinePos(dstStart, 0);
+ if (srcLine.end() > rightSrcBorder) return LinePos(dstStart, 9);
int pixelSize = m_src->pixelSize();
KoMixColorsOp *mixOp = m_src->colorSpace()->mixColorsOp();
const quint8 *defaultPixel = m_src->defaultPixel();
const quint8 *borderPixel = defaultPixel;
quint8 *srcLineBuf = new quint8[pixelSize * (rightSrcBorder - leftSrcBorder)];
int i = leftSrcBorder;
quint8 *bufPtr = srcLineBuf;
T srcIt = tmp::createIterator<T>(m_src, srcLine.start(), line, srcLine.size());
if (m_clampToEdge) {
borderPixel = srcIt->rawData();
}
for (; i < srcLine.start(); i++, bufPtr+=pixelSize) {
memcpy(bufPtr, borderPixel, pixelSize);
}
for (; i < srcLine.end(); i++, bufPtr+=pixelSize) {
quint8 *data = srcIt->rawData();
memcpy(bufPtr, data, pixelSize);
memcpy(data, defaultPixel, pixelSize);
srcIt->nextPixel();
}
if (m_clampToEdge) {
borderPixel = bufPtr - pixelSize;
}
for (; i < rightSrcBorder; i++, bufPtr+=pixelSize) {
memcpy(bufPtr, borderPixel, pixelSize);
}
const quint8 **colors = new const quint8* [buffer->maxSpan()];
T dstIt = tmp::createIterator<T>(m_dst, dstStart, line, dstEnd - dstStart);
for (int i = dstStart; i < dstEnd; i++) {
BlendSpan span = calculateBlendSpan(i, line, buffer);
int bufIndexStart = span.firstBlendPixel - leftSrcBorder;
int bufIndexEnd = bufIndexStart + span.weights->span;
const quint8 **colorsPtr = colors;
for (int j = bufIndexStart; j < bufIndexEnd; j++) {
*(colorsPtr++) = srcLineBuf + j * pixelSize;
}
mixOp->mixColors(colors, span.weights->weight, span.weights->span, dstIt->rawData());
dstIt->nextPixel();
}
delete[] colors;
delete[] srcLineBuf;
- return LinePos(dstStart, dstEnd - dstStart);
+ return LinePos(dstStart, qMax(0, dstEnd - dstStart));
}
private:
int findAntialiasedDstStart(int src_l, qreal support, int line) {
qreal dst = srcToDst(src_l, line);
return !m_clampToEdge ? floor(dst - support) : floor(dst);
}
int findAntialiasedDstEnd(int src_l, qreal support, int line) {
qreal dst = srcToDst(src_l, line);
return !m_clampToEdge ? ceil(dst + support) : ceil(dst);
}
int getLeftSrcNeedBorder(int dst_l, int line, KisFilterWeightsBuffer *buffer) {
BlendSpan span = calculateBlendSpan(dst_l, line, buffer);
return span.firstBlendPixel;
}
int getRightSrcNeedBorder(int dst_l, int line, KisFilterWeightsBuffer *buffer) {
BlendSpan span = calculateBlendSpan(dst_l, line, buffer);
return span.firstBlendPixel + span.weights->span;
}
inline KisFixedPoint l_to_c(KisFixedPoint pixel_l) const {
return pixel_l + KisFixedPoint(qreal(0.5));
}
inline KisFixedPoint c_to_l(KisFixedPoint pixel_c) const {
return pixel_c - KisFixedPoint(qreal(0.5));
}
inline qreal srcToDst(qreal src, int line) const {
return src * m_realScale + m_dx + line * m_shear;
}
inline qreal dstToSrc(qreal dst, int line) const {
return (dst - m_dx - line * m_shear) / m_realScale;
}
private:
KisPaintDeviceSP m_src;
KisPaintDeviceSP m_dst;
qreal m_realScale;
qreal m_shear;
qreal m_dx;
bool m_clampToEdge;
};
#endif /* __KIS_FILTER_WEIGHTS_APPLICATOR_H */
diff --git a/krita/image/kis_filter_weights_buffer.h b/krita/image/kis_filter_weights_buffer.h
index c5d34046a56..0418782a008 100644
--- a/krita/image/kis_filter_weights_buffer.h
+++ b/krita/image/kis_filter_weights_buffer.h
@@ -1,291 +1,291 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_FILTER_WEIGHTS_BUFFER_H
#define __KIS_FILTER_WEIGHTS_BUFFER_H
#include "kis_fixed_point_maths.h"
#include "kis_filter_strategy.h"
#ifdef SANITY_CHECKS_ENABLED
static bool checkForAsymmetricZeros = false;
#define SANITY_CENTER_POSITION() \
do { \
Q_ASSERT(scaledIter >= beginDst); \
Q_ASSERT(scaledIter <= endDst); \
\
if (j == centerIndex) { \
Q_ASSERT(scaledIter == centerSrc); \
} \
} while(0)
#define SANITY_ZEROS() \
do { \
if (checkForAsymmetricZeros) { \
for (int j = 0; j < span; j++) { \
int idx2 = span - j - 1; \
\
if ((m_filterWeights[i].weight[j] && !m_filterWeights[i].weight[idx2]) || \
(!m_filterWeights[i].weight[j] && m_filterWeights[i].weight[idx2])) { \
\
- qDebug() << "*******"; \
- qDebug() << "Non-symmetric zero found:" << centerSrc; \
- qDebug() << "Weight" << j << ":" << m_filterWeights[i].weight[j]; \
- qDebug() << "Weight" << idx2 << ":" << m_filterWeights[i].weight[idx2]; \
+ dbgKrita << "*******"; \
+ dbgKrita << "Non-symmetric zero found:" << centerSrc; \
+ dbgKrita << "Weight" << j << ":" << m_filterWeights[i].weight[j]; \
+ dbgKrita << "Weight" << idx2 << ":" << m_filterWeights[i].weight[idx2]; \
qFatal("Non-symmetric zero -> fail"); \
} \
} \
} \
} while (0)
#define SANITY_CHECKSUM() \
do { \
Q_ASSERT(sum == 255); \
} while (0)
#else
#define SANITY_CENTER_POSITION()
#define SANITY_ZEROS()
#define SANITY_CHECKSUM()
#endif
#ifdef DEBUG_ENABLED
#define DEBUG_ALL() \
do { \
- qDebug() << "************** i =" << i; \
- qDebug() << ppVar(centerSrc); \
- qDebug() << ppVar(centerIndex); \
- qDebug() << ppVar(beginSrc) << ppVar(endSrc); \
- qDebug() << ppVar(beginDst) << ppVar(endDst); \
- qDebug() << ppVar(scaledIter) << ppVar(scaledInc); \
- qDebug() << ppVar(span); \
- qDebug() << "==="; \
+ dbgKrita << "************** i =" << i; \
+ dbgKrita << ppVar(centerSrc); \
+ dbgKrita << ppVar(centerIndex); \
+ dbgKrita << ppVar(beginSrc) << ppVar(endSrc); \
+ dbgKrita << ppVar(beginDst) << ppVar(endDst); \
+ dbgKrita << ppVar(scaledIter) << ppVar(scaledInc); \
+ dbgKrita << ppVar(span); \
+ dbgKrita << "==="; \
} while (0)
#define DEBUG_SAMPLE() \
do { \
- qDebug() << ppVar(scaledIter) << ppVar(t); \
+ dbgKrita << ppVar(scaledIter) << ppVar(t); \
} while (0)
#else
#define DEBUG_ALL() Q_UNUSED(beginDst); Q_UNUSED(endDst)
#define DEBUG_SAMPLE()
#endif
/**
* \class KisFilterWeightsBuffer
*
* Stores the cached values for the weights of neighbouring pixels
* that would form the pixel in a result of resampling. The object of
* this class is created before a pass of the transformation basing on
* the desired scale factor and the filter strategy used for resampling.
*
* Here is an exmple of a calculation of the span for a pixel with
* scale equal to 1.0. The result of the blending will be written into
* the dst(0) pixel, which is marked with '*' sign. Note that all the
* coordinates here are related to the center of the pixel, not to its
* leftmost border as it is common in other systems. The centerSrc
* coordinate represents the offset between the source and the
* destination buffers.
*
* dst-coordinates: the coordinates in the resulting image. The values
* of the filter strategy are calculated in these
* coordinates.
*
* src-coordinates: the coordinates in the source image/buffer. We
* pick integer values from there and calculate their
* dst-position to know their weights.
*
*
* +----+----+----+-- scaledIter (samples, measured in dst pixels,
* | | | | correspond to integers in src)
*
* +---------+-- supportDst == filterStrategy->intSupport()
* | |
* +-- beginDst +-- endDst
* | | |
* | +-- centerDst (always zero)
* | | |
*
* dst: ----|----|----|----|----*----|----|----|----|----|-->
* -4 -3 -2 -1 0 1 2 3 4 5
*
* src: --|----|----|----|----|----|----|----|----|----|---->
* -4 -3 -2 -1 0 1 2 3 4 5
*
* ^ ^ ^
* | | |
* | +-- centerSrc
* | | |
* +-- beginSrc +endSrc
* | | |
* | +---------+-- supportSrc ~= supportDst / realScale
* | |
* +-------------------+-- span (number of integers in the region)
*/
class KisFilterWeightsBuffer
{
public:
struct FilterWeights {
~FilterWeights() {
delete[] weight;
}
qint16 *weight;
int span;
int centerIndex;
};
public:
KisFilterWeightsBuffer(KisFilterStrategy *filterStrategy, qreal realScale) {
Q_ASSERT(realScale > 0);
m_filterWeights = new FilterWeights[256];
m_maxSpan = 0;
m_weightsPositionScale = 1;
KisFixedPoint supportSrc;
KisFixedPoint supportDst;
if (realScale < 1.0) {
supportSrc.from256Frac(filterStrategy->intSupport() / realScale);
supportDst.from256Frac(filterStrategy->intSupport());
m_weightsPositionScale = KisFixedPoint(realScale);
} else {
supportSrc.from256Frac(filterStrategy->intSupport());
supportDst.from256Frac(filterStrategy->intSupport());
}
for (int i = 0; i < 256; i++) {
KisFixedPoint centerSrc;
centerSrc.from256Frac(i);
KisFixedPoint beginDst = -supportDst;
KisFixedPoint endDst = supportDst;
KisFixedPoint beginSrc = -supportSrc - centerSrc / m_weightsPositionScale;
KisFixedPoint endSrc = supportSrc - centerSrc / m_weightsPositionScale;
int span = (2 * supportSrc).toInt() +
(beginSrc.isInteger() && endSrc.isInteger());
int centerIndex = -beginSrc.toInt();
m_filterWeights[i].centerIndex = centerIndex;
m_filterWeights[i].span = span;
m_filterWeights[i].weight = new qint16[span];
m_maxSpan = qMax(m_maxSpan, span);
// in dst coordinate system:
KisFixedPoint scaledIter = centerSrc + beginSrc.toInt() * m_weightsPositionScale;
KisFixedPoint scaledInc = m_weightsPositionScale;
DEBUG_ALL();
int sum = 0;
for (int j = 0; j < span; j++) {
int t = filterStrategy->intValueAt(scaledIter.to256Frac());
m_filterWeights[i].weight[j] = t;
sum += t;
DEBUG_SAMPLE();
SANITY_CENTER_POSITION();
scaledIter += scaledInc;
}
SANITY_ZEROS();
if (sum != 255) {
qreal fixFactor = 255.0 / sum;
sum = 0;
for (int j = 0; j < span; j++) {
int t = qRound(m_filterWeights[i].weight[j] * fixFactor);
m_filterWeights[i].weight[j] = t;
sum += t;
}
}
while (sum != 255) {
int diff = sum < 255 ? 1 : -1;
int index = findMaxIndex(m_filterWeights[i].weight, span);
m_filterWeights[i].weight[index] += diff;
sum += diff;
}
SANITY_CHECKSUM();
}
}
~KisFilterWeightsBuffer() {
delete[] m_filterWeights;
}
/**
* Return a weights buffer for a particular value of offset
*/
FilterWeights* weights(KisFixedPoint pos) const {
return m_filterWeights + pos.to256Frac();
}
/**
* The maximum width of the buffer that would be needed for
* calculation of a pixel value. In other words, the maximum
* number of support pixels that are needed for calculation of a
* single result pixel
*/
int maxSpan() const {
return m_maxSpan;
}
/**
* The scale of the support buffer. Note that it is not always
* equal to the real scale of the transformation due to
* interpolation/blending difference.
*/
KisFixedPoint weightsPositionScale() const {
return m_weightsPositionScale;
}
private:
int findMaxIndex(qint16 *buf, int size) {
int maxValue = buf[0];
int maxIndex = 0;
for (int i = 1; i < size; i++) {
if (buf[i] > maxValue) {
maxValue = buf[i];
maxIndex = i;
}
}
return maxIndex;
}
private:
FilterWeights *m_filterWeights;
int m_maxSpan;
KisFixedPoint m_weightsPositionScale;
};
#endif /* __KIS_FILTER_WEIGHTS_BUFFER_H */
diff --git a/krita/image/kis_grid_interpolation_tools.h b/krita/image/kis_grid_interpolation_tools.h
index 24de41b212a..f807614cea6 100644
--- a/krita/image/kis_grid_interpolation_tools.h
+++ b/krita/image/kis_grid_interpolation_tools.h
@@ -1,600 +1,600 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_GRID_INTERPOLATION_TOOLS_H
#define __KIS_GRID_INTERPOLATION_TOOLS_H
#include <limits>
#include <algorithm>
#include <QImage>
#include "kis_algebra_2d.h"
#include "kis_four_point_interpolator_forward.h"
#include "kis_four_point_interpolator_backward.h"
#include "kis_iterator_ng.h"
#include "kis_random_sub_accessor.h"
//#define DEBUG_PAINTING_POLYGONS
#ifdef DEBUG_PAINTING_POLYGONS
#include <QPainter>
#endif /* DEBUG_PAINTING_POLYGONS */
namespace GridIterationTools {
inline int calcGridDimension(int start, int end, const int pixelPrecision)
{
const int alignmentMask = ~(pixelPrecision - 1);
int alignedStart = (start + pixelPrecision - 1) & alignmentMask;
int alignedEnd = end & alignmentMask;
int size = 0;
if (alignedEnd > alignedStart) {
size = (alignedEnd - alignedStart) / pixelPrecision + 1;
size += alignedStart != start;
size += alignedEnd != end;
} else {
size = 2 + (end - start >= pixelPrecision);
}
return size;
}
inline QSize calcGridSize(const QRect &srcBounds, const int pixelPrecision) {
return QSize(calcGridDimension(srcBounds.x(), srcBounds.right(), pixelPrecision),
calcGridDimension(srcBounds.y(), srcBounds.bottom(), pixelPrecision));
}
template <class ProcessPolygon, class ForwardTransform>
struct CellOp
{
CellOp(ProcessPolygon &_polygonOp, ForwardTransform &_transformOp)
: polygonOp(_polygonOp),
transformOp(_transformOp)
{
}
inline void processPoint(int col, int row,
int prevCol, int prevRow,
int colIndex, int rowIndex) {
QPointF dstPosF = transformOp(QPointF(col, row));
currLinePoints << dstPosF;
if (rowIndex >= 1 && colIndex >= 1) {
QPolygonF srcPolygon;
srcPolygon << QPointF(prevCol, prevRow);
srcPolygon << QPointF(col, prevRow);
srcPolygon << QPointF(col, row);
srcPolygon << QPointF(prevCol, row);
QPolygonF dstPolygon;
dstPolygon << prevLinePoints.at(colIndex - 1);
dstPolygon << prevLinePoints.at(colIndex);
dstPolygon << currLinePoints.at(colIndex);
dstPolygon << currLinePoints.at(colIndex - 1);
polygonOp(srcPolygon, dstPolygon);
}
}
inline void nextLine() {
std::swap(prevLinePoints, currLinePoints);
// we are erasing elements for not free'ing the occupied
// memory, which is more efficient since we are going to fill
// the vector again
currLinePoints.erase(currLinePoints.begin(), currLinePoints.end());
}
QVector<QPointF> prevLinePoints;
QVector<QPointF> currLinePoints;
ProcessPolygon &polygonOp;
ForwardTransform &transformOp;
};
template <class ProcessCell>
void processGrid(ProcessCell &cellOp,
const QRect &srcBounds,
const int pixelPrecision)
{
if (srcBounds.isEmpty()) return;
const int alignmentMask = ~(pixelPrecision - 1);
int prevRow = std::numeric_limits<int>::max();
int prevCol = std::numeric_limits<int>::max();
int rowIndex = 0;
int colIndex = 0;
for (int row = srcBounds.top(); row <= srcBounds.bottom();) {
for (int col = srcBounds.left(); col <= srcBounds.right();) {
cellOp.processPoint(col, row,
prevCol, prevRow,
colIndex, rowIndex);
prevCol = col;
col += pixelPrecision;
colIndex++;
if (col > srcBounds.right() &&
col <= srcBounds.right() + pixelPrecision - 1) {
col = srcBounds.right();
} else {
col &= alignmentMask;
}
}
cellOp.nextLine();
colIndex = 0;
prevRow = row;
row += pixelPrecision;
rowIndex++;
if (row > srcBounds.bottom() &&
row <= srcBounds.bottom() + pixelPrecision - 1) {
row = srcBounds.bottom();
} else {
row &= alignmentMask;
}
}
}
template <class ProcessPolygon, class ForwardTransform>
void processGrid(ProcessPolygon &polygonOp, ForwardTransform &transformOp,
const QRect &srcBounds, const int pixelPrecision)
{
CellOp<ProcessPolygon, ForwardTransform> cellOp(polygonOp, transformOp);
processGrid(cellOp, srcBounds, pixelPrecision);
}
struct PaintDevicePolygonOp
{
PaintDevicePolygonOp(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev)
: m_srcDev(srcDev), m_dstDev(dstDev) {}
void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon) {
this->operator() (srcPolygon, dstPolygon, dstPolygon);
}
void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon, const QPolygonF &clipDstPolygon) {
QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
if (boundRect.isEmpty()) return;
KisSequentialIterator dstIt(m_dstDev, boundRect);
KisRandomSubAccessorSP srcAcc = m_srcDev->createRandomSubAccessor();
KisFourPointInterpolatorBackward interp(srcPolygon, dstPolygon);
int y = boundRect.top();
interp.setY(y);
do {
int newY = dstIt.y();
if (y != newY) {
y = newY;
interp.setY(y);
}
QPointF srcPoint(dstIt.x(), y);
if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
interp.setX(srcPoint.x());
QPointF dstPoint = interp.getValue();
// brain-blowing part:
//
// since the interpolator does the inverted
// transfomation we read data from "dstPoint"
// (which is non-transformed) and write it into
// "srcPoint" (which is transformed position)
srcAcc->moveTo(dstPoint);
srcAcc->sampledOldRawData(dstIt.rawData());
}
} while (dstIt.nextPixel());
}
KisPaintDeviceSP m_srcDev;
KisPaintDeviceSP m_dstDev;
};
struct QImagePolygonOp
{
QImagePolygonOp(const QImage &srcImage, QImage &dstImage,
const QPointF &srcImageOffset,
const QPointF &dstImageOffset)
: m_srcImage(srcImage), m_dstImage(dstImage),
m_srcImageOffset(srcImageOffset),
m_dstImageOffset(dstImageOffset),
m_srcImageRect(m_srcImage.rect()),
m_dstImageRect(m_dstImage.rect())
{
}
void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon) {
this->operator() (srcPolygon, dstPolygon, dstPolygon);
}
void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon, const QPolygonF &clipDstPolygon) {
QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
KisFourPointInterpolatorBackward interp(srcPolygon, dstPolygon);
for (int y = boundRect.top(); y <= boundRect.bottom(); y++) {
interp.setY(y);
for (int x = boundRect.left(); x <= boundRect.right(); x++) {
QPointF srcPoint(x, y);
if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
interp.setX(srcPoint.x());
QPointF dstPoint = interp.getValue();
// about srcPoint/dstPoint hell please see a
// comment in PaintDevicePolygonOp::operator() ()
srcPoint -= m_dstImageOffset;
dstPoint -= m_srcImageOffset;
QPoint srcPointI = srcPoint.toPoint();
QPoint dstPointI = dstPoint.toPoint();
if (!m_dstImageRect.contains(srcPointI)) continue;
if (!m_srcImageRect.contains(dstPointI)) continue;
m_dstImage.setPixel(srcPointI, m_srcImage.pixel(dstPointI));
}
}
}
#ifdef DEBUG_PAINTING_POLYGONS
QPainter gc(&m_dstImage);
gc.setPen(Qt::red);
gc.setOpacity(0.5);
gc.setBrush(Qt::green);
gc.drawPolygon(clipDstPolygon.translated(-m_dstImageOffset));
gc.setBrush(Qt::blue);
//gc.drawPolygon(dstPolygon.translated(-m_dstImageOffset));
#endif /* DEBUG_PAINTING_POLYGONS */
}
const QImage &m_srcImage;
QImage &m_dstImage;
QPointF m_srcImageOffset;
QPointF m_dstImageOffset;
QRect m_srcImageRect;
QRect m_dstImageRect;
};
/*************************************************************/
/* Iteration through precalculated grid */
/*************************************************************/
/**
* A-----B The polygons will be in the following order:
* | |
* | | polygon << A << B << D << C;
* C-----D
*/
inline QVector<int> calculateCellIndexes(int col, int row, const QSize &gridSize)
{
const int tl = col + row * gridSize.width();
const int tr = tl + 1;
const int bl = tl + gridSize.width();
const int br = bl + 1;
QVector<int> cellIndexes;
cellIndexes << tl;
cellIndexes << tr;
cellIndexes << br;
cellIndexes << bl;
return cellIndexes;
}
inline int pointToIndex(const QPoint &cellPt, const QSize &gridSize)
{
return cellPt.x() +
cellPt.y() * gridSize.width();
}
namespace Private {
inline QPoint pointPolygonIndexToColRow(QPoint baseColRow, int index)
{
static QVector<QPoint> pointOffsets;
if (pointOffsets.isEmpty()) {
pointOffsets << QPoint(0,0);
pointOffsets << QPoint(1,0);
pointOffsets << QPoint(1,1);
pointOffsets << QPoint(0,1);
}
return baseColRow + pointOffsets[index];
}
struct PointExtension {
int near;
int far;
};
}
template <class IndexesOp>
bool getOrthogonalPointApproximation(const QPoint &cellPt,
const QVector<QPointF> &originalPoints,
const QVector<QPointF> &transformedPoints,
IndexesOp indexesOp,
QPointF *srcPoint,
QPointF *dstPoint)
{
QVector<Private::PointExtension> extensionPoints;
Private::PointExtension ext;
// left
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
extensionPoints << ext;
}
// top
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
extensionPoints << ext;
}
// right
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
extensionPoints << ext;
}
// bottom
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
extensionPoints << ext;
}
if (extensionPoints.isEmpty()) {
// top-left
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
extensionPoints << ext;
}
// top-right
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {
extensionPoints << ext;
}
// bottom-right
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {
extensionPoints << ext;
}
// bottom-left
if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
(ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {
extensionPoints << ext;
}
}
if (extensionPoints.isEmpty()) {
return false;
}
int numResultPoints = 0;
*srcPoint = indexesOp.getSrcPointForce(cellPt);
*dstPoint = QPointF();
foreach (const Private::PointExtension &ext, extensionPoints) {
QPointF near = transformedPoints[ext.near];
QPointF far = transformedPoints[ext.far];
QPointF nearSrc = originalPoints[ext.near];
QPointF farSrc = originalPoints[ext.far];
QPointF base1 = nearSrc - farSrc;
QPointF base2 = near - far;
QPointF pt = near +
KisAlgebra2D::transformAsBase(*srcPoint - nearSrc, base1, base2);
*dstPoint += pt;
numResultPoints++;
}
*dstPoint /= numResultPoints;
return true;
}
template <class PolygonOp, class IndexesOp>
struct IncompletePolygonPolicy {
static inline bool tryProcessPolygon(int col, int row,
int numExistingPoints,
PolygonOp &polygonOp,
IndexesOp &indexesOp,
const QVector<int> &polygonPoints,
const QVector<QPointF> &originalPoints,
const QVector<QPointF> &transformedPoints)
{
if (numExistingPoints >= 4) return false;
if (numExistingPoints == 0) return true;
QPolygonF srcPolygon;
QPolygonF dstPolygon;
for (int i = 0; i < 4; i++) {
const int index = polygonPoints[i];
if (index >= 0) {
srcPolygon << originalPoints[index];
dstPolygon << transformedPoints[index];
} else {
QPoint cellPt = Private::pointPolygonIndexToColRow(QPoint(col, row), i);
QPointF srcPoint;
QPointF dstPoint;
bool result =
getOrthogonalPointApproximation(cellPt,
originalPoints,
transformedPoints,
indexesOp,
&srcPoint,
&dstPoint);
if (!result) {
- //qDebug() << "*NOT* found any valid point" << allSrcPoints[pointToIndex(cellPt)] << "->" << ppVar(pt);
+ //dbgKrita << "*NOT* found any valid point" << allSrcPoints[pointToIndex(cellPt)] << "->" << ppVar(pt);
break;
} else {
srcPolygon << srcPoint;
dstPolygon << dstPoint;
}
}
}
if (dstPolygon.size() == 4) {
QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
KisFourPointInterpolatorForward forwardTransform(srcPolygon, dstPolygon);
for (int i = 0; i < srcClipPolygon.size(); i++) {
const QPointF newPt = forwardTransform.map(srcClipPolygon[i]);
srcClipPolygon[i] = newPt;
}
polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
}
return true;
}
};
template <class PolygonOp, class IndexesOp>
struct AlwaysCompletePolygonPolicy {
static inline bool tryProcessPolygon(int col, int row,
int numExistingPoints,
PolygonOp &polygonOp,
IndexesOp &indexesOp,
const QVector<int> &polygonPoints,
const QVector<QPointF> &originalPoints,
const QVector<QPointF> &transformedPoints)
{
Q_UNUSED(col);
Q_UNUSED(row);
Q_UNUSED(polygonOp);
Q_UNUSED(indexesOp);
Q_UNUSED(polygonPoints);
Q_UNUSED(originalPoints);
Q_UNUSED(transformedPoints);
KIS_ASSERT_RECOVER_NOOP(numExistingPoints == 4);
return false;
}
};
/**
* There is a weird problem in fetching correct bounds of the polygon.
* If the rightmost (bottommost) point of the polygon is integral, then
* QRectF() will end exactly on it, but when converting into QRect the last
* point will not be taken into account. It happens due to the difference
* between center-point/topleft-point point representation. In many cases
* the latter is expected, but we don't work with it in Qt/Krita.
*/
inline void adjustAlignedPolygon(QPolygonF &polygon)
{
static const qreal eps = 1e-5;
static const QPointF p1(eps, 0.0);
static const QPointF p2(eps, eps);
static const QPointF p3(0.0, eps);
polygon[1] += p1;
polygon[2] += p2;
polygon[3] += p3;
}
template <template <class PolygonOp, class IndexesOp> class IncompletePolygonPolicy,
class PolygonOp,
class IndexesOp>
void iterateThroughGrid(PolygonOp &polygonOp,
IndexesOp &indexesOp,
const QSize &gridSize,
const QVector<QPointF> &originalPoints,
const QVector<QPointF> &transformedPoints)
{
QVector<int> polygonPoints(4);
for (int row = 0; row < gridSize.height() - 1; row++) {
for (int col = 0; col < gridSize.width() - 1; col++) {
int numExistingPoints = 0;
polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
if (!IncompletePolygonPolicy<PolygonOp, IndexesOp>::
tryProcessPolygon(col, row,
numExistingPoints,
polygonOp,
indexesOp,
polygonPoints,
originalPoints,
transformedPoints)) {
QPolygonF srcPolygon;
QPolygonF dstPolygon;
for (int i = 0; i < 4; i++) {
const int index = polygonPoints[i];
srcPolygon << originalPoints[index];
dstPolygon << transformedPoints[index];
}
adjustAlignedPolygon(srcPolygon);
adjustAlignedPolygon(dstPolygon);
polygonOp(srcPolygon, dstPolygon);
}
}
}
}
}
#endif /* __KIS_GRID_INTERPOLATION_TOOLS_H */
diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index b8bc8889851..602d3b25fac 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -1,1940 +1,1940 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_image.h"
#include <KoConfig.h> // WORDS_BIGENDIAN
#include <stdlib.h>
#include <math.h>
#include <QImage>
#include <QPainter>
#include <QSize>
#include <QDateTime>
#include <QRect>
#include <QRegion>
#include <QtConcurrent>
#include <klocale.h>
#include "KoColorSpaceRegistry.h"
#include "KoColor.h"
#include "KoColorConversionTransformation.h"
#include "KoColorProfile.h"
#include <KoCompositeOpRegistry.h>
#include "recorder/kis_action_recorder.h"
#include "kis_adjustment_layer.h"
#include "kis_annotation.h"
#include "kis_change_profile_visitor.h"
#include "kis_colorspace_convert_visitor.h"
#include "kis_count_visitor.h"
#include "kis_filter_strategy.h"
#include "kis_group_layer.h"
#include "commands/kis_image_commands.h"
#include "kis_layer.h"
#include "kis_meta_data_merge_strategy_registry.h"
#include "kis_name_server.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
#include "kis_perspective_grid.h"
#include "kis_selection.h"
#include "kis_transaction.h"
#include "kis_types.h"
#include "kis_meta_data_merge_strategy.h"
#include "kis_memory_statistics_server.h"
#include "kis_image_config.h"
#include "kis_update_scheduler.h"
#include "kis_image_signal_router.h"
#include "kis_stroke_strategy.h"
#include "kis_image_barrier_locker.h"
#include "kis_undo_stores.h"
#include "kis_legacy_undo_adapter.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_transform_worker.h"
#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
#include "processing/kis_crop_selections_processing_visitor.h"
#include "processing/kis_transform_processing_visitor.h"
#include "commands_new/kis_image_resize_command.h"
#include "commands_new/kis_image_set_resolution_command.h"
#include "commands_new/kis_activate_selection_mask_command.h"
#include "kis_composite_progress_proxy.h"
#include "kis_layer_composition.h"
#include "kis_wrapped_rect.h"
#include "kis_crop_saved_extra_data.h"
#include "kis_layer_projection_plane.h"
#include "kis_update_time_monitor.h"
#include <QtCore>
#include <boost/bind.hpp>
// #define SANITY_CHECKS
#ifdef SANITY_CHECKS
#define SANITY_CHECK_LOCKED(name) \
if (!locked()) warnKrita() << "Locking policy failed:" << name \
<< "has been called without the image" \
"being locked";
#else
#define SANITY_CHECK_LOCKED(name)
#endif
class KisImage::KisImagePrivate
{
public:
KisImagePrivate(KisImage *_q) : q(_q) {}
KisImage *q;
quint32 lockCount;
KisPerspectiveGrid* perspectiveGrid;
qint32 width;
qint32 height;
double xres;
double yres;
const KoColorSpace * colorSpace;
KisSelectionSP deselectedGlobalSelection;
KisGroupLayerSP rootLayer; // The layers are contained in here
QList<KisLayer*> dirtyLayers; // for thumbnails
QList<KisLayerComposition*> compositions;
KisNodeSP isolatedRootNode;
bool wrapAroundModePermitted;
KisNameServer *nserver;
KisUndoStore *undoStore;
KisUndoAdapter *legacyUndoAdapter;
KisPostExecutionUndoAdapter *postExecutionUndoAdapter;
KisActionRecorder *recorder;
vKisAnnotationSP annotations;
QAtomicInt disableUIUpdateSignals;
QAtomicInt disableDirtyRequests;
KisImageSignalRouter *signalRouter;
KisUpdateScheduler *scheduler;
KisCompositeProgressProxy *compositeProgressProxy;
bool startProjection;
bool tryCancelCurrentStrokeAsync();
void notifyProjectionUpdatedInPatches(const QRect &rc);
};
KisImage::KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace * colorSpace, const QString& name, bool startProjection)
: QObject(0)
, KisShared()
, m_d(new KisImagePrivate(this))
{
setObjectName(name);
dbgImage << "creating" << name;
m_d->startProjection = startProjection;
if (colorSpace == 0) {
colorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
m_d->lockCount = 0;
m_d->perspectiveGrid = 0;
m_d->scheduler = 0;
m_d->wrapAroundModePermitted = false;
m_d->compositeProgressProxy = new KisCompositeProgressProxy();
{
KisImageConfig cfg;
m_d->scheduler = new KisUpdateScheduler(this);
if (cfg.enableProgressReporting()) {
m_d->scheduler->setProgressProxy(m_d->compositeProgressProxy);
}
}
m_d->signalRouter = new KisImageSignalRouter(this);
if (!undoStore) {
undoStore = new KisDumbUndoStore();
}
m_d->undoStore = undoStore;
m_d->legacyUndoAdapter = new KisLegacyUndoAdapter(m_d->undoStore, this);
m_d->postExecutionUndoAdapter = new KisPostExecutionUndoAdapter(m_d->undoStore, this);
m_d->nserver = new KisNameServer(1);
m_d->colorSpace = colorSpace;
setRootLayer(new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8));
m_d->xres = 1.0;
m_d->yres = 1.0;
m_d->width = width;
m_d->height = height;
m_d->recorder = new KisActionRecorder(this);
connect(this, SIGNAL(sigImageModified()), KisMemoryStatisticsServer::instance(), SLOT(notifyImageChanged()));
}
KisImage::~KisImage()
{
dbgImage << "deleting kisimage" << objectName();
/**
* Request the tools to end currently running strokes
*/
waitForDone();
/**
* First delete the nodes, while strokes
* and undo are still alive
*/
m_d->rootLayer = 0;
KisUpdateScheduler *scheduler = m_d->scheduler;
m_d->scheduler = 0;
delete scheduler;
delete m_d->postExecutionUndoAdapter;
delete m_d->legacyUndoAdapter;
delete m_d->undoStore;
delete m_d->compositeProgressProxy;
delete m_d->signalRouter;
delete m_d->perspectiveGrid;
delete m_d->nserver;
delete m_d;
disconnect(); // in case Qt gets confused
}
void KisImage::aboutToAddANode(KisNode *parent, int index)
{
KisNodeGraphListener::aboutToAddANode(parent, index);
SANITY_CHECK_LOCKED("aboutToAddANode");
}
void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
{
KisNodeGraphListener::nodeHasBeenAdded(parent, index);
SANITY_CHECK_LOCKED("nodeHasBeenAdded");
m_d->signalRouter->emitNodeHasBeenAdded(parent, index);
KisNodeSP newNode = parent->at(index);
if (!dynamic_cast<KisSelectionMask*>(newNode.data())) {
stopIsolatedMode();
}
}
void KisImage::aboutToRemoveANode(KisNode *parent, int index)
{
KisNodeSP deletedNode = parent->at(index);
if (!dynamic_cast<KisSelectionMask*>(deletedNode.data())) {
stopIsolatedMode();
}
KisNodeGraphListener::aboutToRemoveANode(parent, index);
SANITY_CHECK_LOCKED("aboutToRemoveANode");
m_d->signalRouter->emitAboutToRemoveANode(parent, index);
}
void KisImage::nodeChanged(KisNode* node)
{
KisNodeGraphListener::nodeChanged(node);
requestStrokeEnd();
m_d->signalRouter->emitNodeChanged(node);
}
KisSelectionSP KisImage::globalSelection() const
{
KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
if (selectionMask) {
return selectionMask->selection();
} else {
return 0;
}
}
void KisImage::setGlobalSelection(KisSelectionSP globalSelection)
{
KisSelectionMaskSP selectionMask = m_d->rootLayer->selectionMask();
if (!globalSelection) {
if (selectionMask) {
removeNode(selectionMask);
}
}
else {
if (!selectionMask) {
selectionMask = new KisSelectionMask(this);
selectionMask->initSelection(m_d->rootLayer);
addNode(selectionMask);
// If we do not set the selection now, the setActive call coming next
// can be very, very expensive, depending on the size of the image.
selectionMask->setSelection(globalSelection);
selectionMask->setActive(true);
}
else {
selectionMask->setSelection(globalSelection);
}
Q_ASSERT(m_d->rootLayer->childCount() > 0);
Q_ASSERT(m_d->rootLayer->selectionMask());
}
m_d->deselectedGlobalSelection = 0;
m_d->legacyUndoAdapter->emitSelectionChanged();
}
void KisImage::deselectGlobalSelection()
{
KisSelectionSP savedSelection = globalSelection();
setGlobalSelection(0);
m_d->deselectedGlobalSelection = savedSelection;
}
bool KisImage::canReselectGlobalSelection()
{
return m_d->deselectedGlobalSelection;
}
void KisImage::reselectGlobalSelection()
{
if(m_d->deselectedGlobalSelection) {
setGlobalSelection(m_d->deselectedGlobalSelection);
}
}
QString KisImage::nextLayerName() const
{
if (m_d->nserver->currentSeed() == 0) {
m_d->nserver->number();
return i18n("background");
}
return i18n("Layer %1", m_d->nserver->number());
}
void KisImage::rollBackLayerName()
{
m_d->nserver->rollback();
}
KisCompositeProgressProxy* KisImage::compositeProgressProxy()
{
return m_d->compositeProgressProxy;
}
bool KisImage::locked() const
{
return m_d->lockCount != 0;
}
void KisImage::barrierLock()
{
if (!locked()) {
requestStrokeEnd();
if (m_d->scheduler) {
m_d->scheduler->barrierLock();
}
}
m_d->lockCount++;
}
bool KisImage::tryBarrierLock()
{
bool result = true;
if (!locked()) {
if (m_d->scheduler) {
result = m_d->scheduler->tryBarrierLock();
}
}
if (result) {
m_d->lockCount++;
}
return result;
}
void KisImage::lock()
{
if (!locked()) {
requestStrokeEnd();
if (m_d->scheduler) {
m_d->scheduler->lock();
}
}
m_d->lockCount++;
}
void KisImage::unlock()
{
Q_ASSERT(locked());
if (locked()) {
m_d->lockCount--;
if (m_d->lockCount == 0) {
if (m_d->scheduler) {
m_d->scheduler->unlock();
}
}
}
}
void KisImage::blockUpdates()
{
m_d->scheduler->blockUpdates();
}
void KisImage::unblockUpdates()
{
m_d->scheduler->unblockUpdates();
}
void KisImage::setSize(const QSize& size)
{
m_d->width = size.width();
m_d->height = size.height();
}
void KisImage::resizeImageImpl(const QRect& newRect, bool cropLayers)
{
if (newRect == bounds() && !cropLayers) return;
KUndo2MagicString actionName = cropLayers ?
kundo2_i18n("Crop Image") :
kundo2_i18n("Resize Image");
KisImageSignalVector emitSignals;
emitSignals << ComplexSizeChangedSignal(newRect, newRect.size());
emitSignals << ModifiedSignal;
KisCropSavedExtraData *extraData =
new KisCropSavedExtraData(cropLayers ?
KisCropSavedExtraData::CROP_IMAGE :
KisCropSavedExtraData::RESIZE_IMAGE,
newRect);
KisProcessingApplicator applicator(this, m_d->rootLayer,
KisProcessingApplicator::RECURSIVE |
KisProcessingApplicator::NO_UI_UPDATES,
emitSignals, actionName, extraData);
if (cropLayers || !newRect.topLeft().isNull()) {
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(newRect, cropLayers, true);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
}
applicator.applyCommand(new KisImageResizeCommand(this, newRect.size()));
applicator.end();
}
void KisImage::resizeImage(const QRect& newRect)
{
resizeImageImpl(newRect, false);
}
void KisImage::cropImage(const QRect& newRect)
{
resizeImageImpl(newRect, true);
}
void KisImage::cropNode(KisNodeSP node, const QRect& newRect)
{
bool isLayer = dynamic_cast<KisLayer*>(node.data());
KUndo2MagicString actionName = isLayer ?
kundo2_i18n("Crop Layer") :
kundo2_i18n("Crop Mask");
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisCropSavedExtraData *extraData =
new KisCropSavedExtraData(KisCropSavedExtraData::CROP_LAYER,
newRect, node);
KisProcessingApplicator applicator(this, node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName, extraData);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(newRect, true, false);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
void KisImage::scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
{
bool resolutionChanged = xres != xRes() && yres != yRes();
bool sizeChanged = size != this->size();
if (!resolutionChanged && !sizeChanged) return;
KisImageSignalVector emitSignals;
if (resolutionChanged) emitSignals << ResolutionChangedSignal;
if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), size);
emitSignals << ModifiedSignal;
KUndo2MagicString actionName = sizeChanged ?
kundo2_i18n("Scale Image") :
kundo2_i18n("Change Image Resolution");
KisProcessingApplicator::ProcessingFlags signalFlags =
(resolutionChanged || sizeChanged) ?
KisProcessingApplicator::NO_UI_UPDATES :
KisProcessingApplicator::NONE;
KisProcessingApplicator applicator(this, m_d->rootLayer,
KisProcessingApplicator::RECURSIVE | signalFlags,
emitSignals, actionName);
qreal sx = qreal(size.width()) / this->size().width();
qreal sy = qreal(size.height()) / this->size().height();
QTransform shapesCorrection;
if (resolutionChanged) {
shapesCorrection = QTransform::fromScale(xRes() / xres, yRes() / yres);
}
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(sx, sy,
0, 0,
QPointF(),
0,
0, 0,
filterStrategy,
shapesCorrection);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
if (resolutionChanged) {
KUndo2Command *parent =
new KisResetShapesCommand(m_d->rootLayer);
new KisImageSetResolutionCommand(this, xres, yres, parent);
applicator.applyCommand(parent);
}
if (sizeChanged) {
applicator.applyCommand(new KisImageResizeCommand(this, size));
}
applicator.end();
}
void KisImage::scaleNode(KisNodeSP node, qreal sx, qreal sy, KisFilterStrategy *filterStrategy)
{
KUndo2MagicString actionName(kundo2_i18n("Scale Layer"));
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(this, node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(sx, sy,
0, 0,
QPointF(),
0,
0, 0,
filterStrategy);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
void KisImage::rotateImpl(const KUndo2MagicString &actionName,
KisNodeSP rootNode,
bool resizeImage,
double radians)
{
QPointF offset;
QSize newSize;
{
KisTransformWorker worker(0,
1.0, 1.0,
0, 0, 0, 0,
radians,
0, 0, 0, 0);
QTransform transform = worker.transform();
if (resizeImage) {
QRect newRect = transform.mapRect(bounds());
newSize = newRect.size();
offset = -newRect.topLeft();
}
else {
QPointF origin = QRectF(rootNode->exactBounds()).center();
newSize = size();
offset = -(transform.map(origin) - origin);
}
}
bool sizeChanged = resizeImage &&
(newSize.width() != width() || newSize.height() != height());
// These signals will be emitted after processing is done
KisImageSignalVector emitSignals;
if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
emitSignals << ModifiedSignal;
// These flags determine whether updates are transferred to the UI during processing
KisProcessingApplicator::ProcessingFlags signalFlags =
sizeChanged ?
KisProcessingApplicator::NO_UI_UPDATES :
KisProcessingApplicator::NONE;
KisProcessingApplicator applicator(this, rootNode,
KisProcessingApplicator::RECURSIVE | signalFlags,
emitSignals, actionName);
KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bicubic");
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0,
QPointF(),
radians,
offset.x(), offset.y(),
filter);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
if (sizeChanged) {
applicator.applyCommand(new KisImageResizeCommand(this, newSize));
}
applicator.end();
}
void KisImage::rotateImage(double radians)
{
rotateImpl(kundo2_i18n("Rotate Image"), root(), true, radians);
}
void KisImage::rotateNode(KisNodeSP node, double radians)
{
rotateImpl(kundo2_i18n("Rotate Layer"), node, false, radians);
}
void KisImage::shearImpl(const KUndo2MagicString &actionName,
KisNodeSP rootNode,
bool resizeImage,
double angleX, double angleY,
const QPointF &origin)
{
//angleX, angleY are in degrees
const qreal pi = 3.1415926535897932385;
const qreal deg2rad = pi / 180.0;
qreal tanX = tan(angleX * deg2rad);
qreal tanY = tan(angleY * deg2rad);
QPointF offset;
QSize newSize;
{
KisTransformWorker worker(0,
1.0, 1.0,
tanX, tanY, origin.x(), origin.y(),
0,
0, 0, 0, 0);
QRect newRect = worker.transform().mapRect(bounds());
newSize = newRect.size();
if (resizeImage) offset = -newRect.topLeft();
}
if (newSize == size()) return;
KisImageSignalVector emitSignals;
if (resizeImage) emitSignals << ComplexSizeChangedSignal(bounds(), newSize);
emitSignals << ModifiedSignal;
KisProcessingApplicator::ProcessingFlags signalFlags =
KisProcessingApplicator::RECURSIVE;
if (resizeImage) signalFlags |= KisProcessingApplicator::NO_UI_UPDATES;
KisProcessingApplicator applicator(this, rootNode,
signalFlags,
emitSignals, actionName);
KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->value("Bilinear");
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(1.0, 1.0,
tanX, tanY, origin,
0,
offset.x(), offset.y(),
filter);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
if (resizeImage) {
applicator.applyCommand(new KisImageResizeCommand(this, newSize));
}
applicator.end();
}
void KisImage::shearNode(KisNodeSP node, double angleX, double angleY)
{
QPointF shearOrigin = QRectF(bounds()).center();
shearImpl(kundo2_i18n("Shear layer"), node, false,
angleX, angleY, shearOrigin);
}
void KisImage::shear(double angleX, double angleY)
{
shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true,
angleX, angleY, QPointF());
}
void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
{
if (!dstColorSpace) return;
const KoColorSpace *srcColorSpace = m_d->colorSpace;
undoAdapter()->beginMacro(kundo2_i18n("Convert Image Color Space"));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
KisColorSpaceConvertVisitor visitor(this, srcColorSpace, dstColorSpace, renderingIntent, conversionFlags);
m_d->rootLayer->accept(visitor);
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
undoAdapter()->endMacro();
setModified();
}
bool KisImage::assignImageProfile(const KoColorProfile *profile)
{
if (!profile) return false;
const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
const KoColorSpace *srcCs = colorSpace();
if (!dstCs) return false;
m_d->colorSpace = dstCs;
KisChangeProfileVisitor visitor(srcCs, dstCs);
return m_d->rootLayer->accept(visitor);
}
void KisImage::convertProjectionColorSpace(const KoColorSpace *dstColorSpace)
{
if (*m_d->colorSpace == *dstColorSpace) return;
undoAdapter()->beginMacro(kundo2_i18n("Convert Projection Color Space"));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), true));
undoAdapter()->addCommand(new KisImageSetProjectionColorSpaceCommand(KisImageWSP(this), dstColorSpace));
undoAdapter()->addCommand(new KisImageLockCommand(KisImageWSP(this), false));
undoAdapter()->endMacro();
setModified();
}
void KisImage::setProjectionColorSpace(const KoColorSpace * colorSpace)
{
m_d->colorSpace = colorSpace;
m_d->rootLayer->resetCache();
m_d->signalRouter->emitNotification(ColorSpaceChangedSignal);
}
const KoColorSpace * KisImage::colorSpace() const
{
return m_d->colorSpace;
}
const KoColorProfile * KisImage::profile() const
{
return colorSpace()->profile();
}
double KisImage::xRes() const
{
return m_d->xres;
}
double KisImage::yRes() const
{
return m_d->yres;
}
void KisImage::setResolution(double xres, double yres)
{
m_d->xres = xres;
m_d->yres = yres;
m_d->signalRouter->emitNotification(ResolutionChangedSignal);
}
QPointF KisImage::documentToPixel(const QPointF &documentCoord) const
{
return QPointF(documentCoord.x() * xRes(), documentCoord.y() * yRes());
}
QPoint KisImage::documentToIntPixel(const QPointF &documentCoord) const
{
QPointF pixelCoord = documentToPixel(documentCoord);
return QPoint((int)pixelCoord.x(), (int)pixelCoord.y());
}
QRectF KisImage::documentToPixel(const QRectF &documentRect) const
{
return QRectF(documentToPixel(documentRect.topLeft()), documentToPixel(documentRect.bottomRight()));
}
QRect KisImage::documentToIntPixel(const QRectF &documentRect) const
{
return documentToPixel(documentRect).toAlignedRect();
}
QPointF KisImage::pixelToDocument(const QPointF &pixelCoord) const
{
return QPointF(pixelCoord.x() / xRes(), pixelCoord.y() / yRes());
}
QPointF KisImage::pixelToDocument(const QPoint &pixelCoord) const
{
return QPointF((pixelCoord.x() + 0.5) / xRes(), (pixelCoord.y() + 0.5) / yRes());
}
QRectF KisImage::pixelToDocument(const QRectF &pixelCoord) const
{
return QRectF(pixelToDocument(pixelCoord.topLeft()), pixelToDocument(pixelCoord.bottomRight()));
}
qint32 KisImage::width() const
{
return m_d->width;
}
qint32 KisImage::height() const
{
return m_d->height;
}
KisGroupLayerSP KisImage::rootLayer() const
{
Q_ASSERT(m_d->rootLayer);
return m_d->rootLayer;
}
KisPaintDeviceSP KisImage::projection() const
{
if (m_d->isolatedRootNode) {
return m_d->isolatedRootNode->projection();
}
Q_ASSERT(m_d->rootLayer);
KisPaintDeviceSP projection = m_d->rootLayer->projection();
Q_ASSERT(projection);
return projection;
}
qint32 KisImage::nlayers() const
{
QStringList list;
list << "KisLayer";
KisCountVisitor visitor(list, KoProperties());
m_d->rootLayer->accept(visitor);
return visitor.count();
}
qint32 KisImage::nHiddenLayers() const
{
QStringList list;
list << "KisLayer";
KoProperties properties;
properties.setProperty("visible", false);
KisCountVisitor visitor(list, properties);
m_d->rootLayer->accept(visitor);
return visitor.count();
}
QRect realNodeExactBounds(KisNodeSP rootNode, QRect currentRect = QRect())
{
KisNodeSP node = rootNode->firstChild();
while(node) {
currentRect |= realNodeExactBounds(node, currentRect);
node = node->nextSibling();
}
// TODO: it would be better to count up changeRect inside
// node's extent() method
currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds());
return currentRect;
}
void KisImage::refreshHiddenArea(KisNodeSP rootNode, const QRect &preparedArea)
{
QRect realNodeRect = realNodeExactBounds(rootNode);
if (!preparedArea.contains(realNodeRect)) {
QRegion dirtyRegion = realNodeRect;
dirtyRegion -= preparedArea;
foreach(const QRect &rc, dirtyRegion.rects()) {
refreshGraph(rootNode, rc, realNodeRect);
}
}
}
void KisImage::flatten()
{
KisGroupLayerSP oldRootLayer = m_d->rootLayer;
KisGroupLayerSP newRootLayer =
new KisGroupLayer(this, "root", OPACITY_OPAQUE_U8);
refreshHiddenArea(oldRootLayer, bounds());
lock();
KisPaintDeviceSP projectionCopy = new KisPaintDevice(oldRootLayer->projection()->colorSpace());
projectionCopy->makeCloneFrom(oldRootLayer->projection(), oldRootLayer->exactBounds());
unlock();
KisPaintLayerSP flattenLayer =
new KisPaintLayer(this, nextLayerName(), OPACITY_OPAQUE_U8, projectionCopy);
Q_CHECK_PTR(flattenLayer);
addNode(flattenLayer, newRootLayer, 0);
undoAdapter()->beginMacro(kundo2_i18n("Flatten Image"));
// NOTE: KisImageChangeLayersCommand performs all the locking for us
undoAdapter()->addCommand(new KisImageChangeLayersCommand(KisImageWSP(this), oldRootLayer, newRootLayer));
undoAdapter()->endMacro();
setModified();
}
bool checkIsSourceForClone(KisNodeSP src, const QList<KisNodeSP> &nodes)
{
foreach (KisNodeSP node, nodes) {
if (node == src) continue;
KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
if (clone && KisNodeSP(clone->copyFrom()) == src) {
return true;
}
}
return false;
}
void KisImage::safeRemoveMultipleNodes(QList<KisNodeSP> nodes)
{
while (!nodes.isEmpty()) {
QList<KisNodeSP>::iterator it = nodes.begin();
while (it != nodes.end()) {
if (!checkIsSourceForClone(*it, nodes)) {
KisNodeSP node = *it;
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, node));
it = nodes.erase(it);
} else {
++it;
}
}
}
}
bool checkIsChildOf(KisNodeSP node, const QList<KisNodeSP> &parents)
{
QList<KisNodeSP> nodeParents;
KisNodeSP parent = node->parent();
while (parent) {
nodeParents << parent;
parent = parent->parent();
}
foreach(KisNodeSP perspectiveParent, parents) {
if (nodeParents.contains(perspectiveParent)) {
return true;
}
}
return false;
}
void filterMergableNodes(QList<KisNodeSP> &nodes)
{
QList<KisNodeSP>::iterator it = nodes.begin();
while (it != nodes.end()) {
if (!dynamic_cast<KisLayer*>(it->data()) ||
checkIsChildOf(*it, nodes)) {
- qDebug() << "Skipping node" << ppVar((*it)->name());
+ dbgKrita << "Skipping node" << ppVar((*it)->name());
it = nodes.erase(it);
} else {
++it;
}
}
}
void sortMergableNodes(KisNodeSP root, QList<KisNodeSP> &inputNodes, QList<KisNodeSP> &outputNodes)
{
QList<KisNodeSP>::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
if (it != inputNodes.end()) {
outputNodes << *it;
inputNodes.erase(it);
}
if (inputNodes.isEmpty()) {
return;
}
KisNodeSP child = root->firstChild();
while (child) {
sortMergableNodes(child, inputNodes, outputNodes);
child = child->nextSibling();
}
/**
* By the end of recursion \p inputNodes must be empty
*/
KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
}
void fetchSelectionMasks(QList<KisNodeSP> mergedNodes, QVector<KisSelectionMaskSP> &selectionMasks)
{
foreach (KisNodeSP node, mergedNodes) {
KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
KisSelectionMaskSP mask;
if (layer && (mask = layer->selectionMask())) {
selectionMasks.append(mask);
}
}
}
void reparentSelectionMasks(KisLayerSP newLayer, const QVector<KisSelectionMaskSP> &selectionMasks)
{
KisImageSP image = newLayer->image();
foreach (KisSelectionMaskSP mask, selectionMasks) {
image->undoAdapter()->addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild()));
image->undoAdapter()->addCommand(new KisActivateSelectionMaskCommand(mask, false));
}
}
KisNodeSP tryMergeSelectionMasks(KisImageSP image, QList<KisNodeSP> mergedNodes)
{
if (mergedNodes.isEmpty()) return 0;
QList<KisSelectionMaskSP> selectionMasks;
foreach (KisNodeSP node, mergedNodes) {
KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(node.data());
if (!mask) return 0;
selectionMasks.append(mask);
}
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(selectionMasks.first()->parent().data());
KIS_ASSERT_RECOVER(parentLayer) { return 0; }
KisSelectionSP selection = new KisSelection();
foreach (KisMaskSP mask, selectionMasks) {
selection->pixelSelection()->applySelection(
mask->selection()->pixelSelection(), SELECTION_ADD);
}
image->undoAdapter()->beginMacro(kundo2_i18n("Merge Selection Masks"));
KisSelectionMaskSP mergedMask = new KisSelectionMask(image);
mergedMask->initSelection(parentLayer);
image->undoAdapter()->addCommand(new KisImageLayerAddCommand(image, mergedMask, parentLayer, parentLayer->lastChild()));
mergedMask->setSelection(selection);
image->undoAdapter()->addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
image->safeRemoveMultipleNodes(mergedNodes);
image->undoAdapter()->endMacro();
return mergedMask;
}
KisNodeSP KisImage::mergeMultipleLayers(QList<KisNodeSP> mergedNodes, KisNodeSP putAfter)
{
{
KisNodeSP mask;
if ((mask = tryMergeSelectionMasks(this, mergedNodes))) {
return mask;
}
}
filterMergableNodes(mergedNodes);
{
QList<KisNodeSP> tempNodes;
qSwap(mergedNodes, tempNodes);
sortMergableNodes(m_d->rootLayer, tempNodes, mergedNodes);
}
if (mergedNodes.size() <= 1) return KisNodeSP();
// fetch selection masks to move them into the destination layer
QVector<KisSelectionMaskSP> selectionMasks;
fetchSelectionMasks(mergedNodes, selectionMasks);
foreach (KisNodeSP layer, mergedNodes) {
refreshHiddenArea(layer, bounds());
}
KisPaintDeviceSP mergedDevice = new KisPaintDevice(colorSpace());
KisPainter gc(mergedDevice);
{
KisImageBarrierLocker l(this);
foreach (KisNodeSP layer, mergedNodes) {
QRect rc = layer->exactBounds() | bounds();
layer->projectionPlane()->apply(&gc, rc);
}
}
const QString mergedLayerSuffix = i18n("Merged");
QString mergedLayerName = mergedNodes.first()->name();
if (!mergedLayerName.endsWith(mergedLayerSuffix)) {
mergedLayerName = QString("%1 %2")
.arg(mergedLayerName).arg(mergedLayerSuffix);
}
KisLayerSP newLayer = new KisPaintLayer(this, mergedLayerName, OPACITY_OPAQUE_U8, mergedDevice);
undoAdapter()->beginMacro(kundo2_i18n("Merge Selected Nodes"));
if (!putAfter) {
putAfter = mergedNodes.last();
}
// Add the new merged node on top of the active node -- checking
// whether the parent is going to be deleted
KisNodeSP parent = putAfter->parent();
while (mergedNodes.contains(parent)) {
parent = parent->parent();
}
if (parent == putAfter->parent()) {
undoAdapter()->addCommand(new KisImageLayerAddCommand(this, newLayer, parent, putAfter));
} else {
undoAdapter()->addCommand(new KisImageLayerAddCommand(this, newLayer, parent, parent->lastChild()));
}
// reparent selection masks into the newly created node
reparentSelectionMasks(newLayer, selectionMasks);
safeRemoveMultipleNodes(mergedNodes);
undoAdapter()->endMacro();
return newLayer;
}
KisLayerSP KisImage::mergeDown(KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
{
if (!layer->prevSibling()) return 0;
// XXX: this breaks if we allow free mixing of masks and layers
KisLayerSP prevLayer = dynamic_cast<KisLayer*>(layer->prevSibling().data());
if (!prevLayer) return 0;
refreshHiddenArea(layer, bounds());
refreshHiddenArea(prevLayer, bounds());
QVector<KisSelectionMaskSP> selectionMasks;
{
QList<KisNodeSP> mergedNodes;
mergedNodes << layer;
mergedNodes << prevLayer;
fetchSelectionMasks(mergedNodes, selectionMasks);
}
QRect layerProjectionExtent = this->projection()->extent();
QRect prevLayerProjectionExtent = prevLayer->projection()->extent();
// actual merging done by KisLayer::createMergedLayer (or specialized decendant)
KisLayerSP mergedLayer = layer->createMergedLayer(prevLayer);
Q_CHECK_PTR(mergedLayer);
// Merge meta data
QList<const KisMetaData::Store*> srcs;
srcs.append(prevLayer->metaData());
srcs.append(layer->metaData());
QList<double> scores;
int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
double norm = qMax(prevLayerArea, layerArea);
scores.append(prevLayerArea / norm);
scores.append(layerArea / norm);
strategy->merge(mergedLayer->metaData(), srcs, scores);
KisNodeSP parent = layer->parent(); // parent is set to null when the layer is removed from the node
dbgImage << ppVar(parent);
// FIXME: "Merge Down"?
undoAdapter()->beginMacro(kundo2_i18n("Merge with Layer Below"));
undoAdapter()->addCommand(new KisImageLayerAddCommand(this, mergedLayer, parent, layer));
// reparent selection masks into the newly created node
reparentSelectionMasks(mergedLayer, selectionMasks);
safeRemoveTwoNodes(layer, prevLayer);
undoAdapter()->endMacro();
return mergedLayer;
}
/**
* The removal of two nodes in one go may be a bit tricky, because one
* of them may be the clone of another. If we remove the source of a
* clone layer, it will reincarnate into a paint layer. In this case
* the pointer to the second layer will be lost.
*
* That's why we need to care about the order of the nodes removal:
* the clone --- first, the source --- last.
*/
void KisImage::safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2)
{
KisCloneLayer *clone1 = dynamic_cast<KisCloneLayer*>(node1.data());
if (clone1 && KisNodeSP(clone1->copyFrom()) == node2) {
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, node1));
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, node2));
} else {
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, node2));
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, node1));
}
}
KisLayerSP KisImage::flattenLayer(KisLayerSP layer)
{
if (!layer->firstChild()) return layer;
refreshHiddenArea(layer, bounds());
bool resetComposition = false;
KisPaintDeviceSP mergedDevice;
lock();
if (layer->layerStyle()) {
mergedDevice = new KisPaintDevice(layer->colorSpace());
mergedDevice->prepareClone(layer->projection());
QRect updateRect = layer->projection()->extent() | bounds();
KisPainter gc(mergedDevice);
layer->projectionPlane()->apply(&gc, updateRect);
resetComposition = true;
} else {
mergedDevice = new KisPaintDevice(*layer->projection());
}
unlock();
KisPaintLayerSP newLayer = new KisPaintLayer(this, layer->name(), layer->opacity(), mergedDevice);
if (!resetComposition) {
newLayer->setCompositeOp(layer->compositeOp()->id());
newLayer->setChannelFlags(layer->channelFlags());
}
undoAdapter()->beginMacro(kundo2_i18n("Flatten Layer"));
undoAdapter()->addCommand(new KisImageLayerAddCommand(this, newLayer, layer->parent(), layer));
undoAdapter()->addCommand(new KisImageLayerRemoveCommand(this, layer));
QList<const KisMetaData::Store*> srcs;
srcs.append(layer->metaData());
const KisMetaData::MergeStrategy* strategy = KisMetaData::MergeStrategyRegistry::instance()->get("Smart");
QList<double> scores;
scores.append(1.0); //Just give some score, there only is one layer
strategy->merge(newLayer->metaData(), srcs, scores);
undoAdapter()->endMacro();
return newLayer;
}
void KisImage::setModified()
{
m_d->signalRouter->emitNotification(ModifiedSignal);
}
QImage KisImage::convertToQImage(QRect imageRect,
const KoColorProfile * profile)
{
qint32 x;
qint32 y;
qint32 w;
qint32 h;
imageRect.getRect(&x, &y, &w, &h);
return convertToQImage(x, y, w, h, profile);
}
QImage KisImage::convertToQImage(qint32 x,
qint32 y,
qint32 w,
qint32 h,
const KoColorProfile * profile)
{
KisPaintDeviceSP dev = projection();
if (!dev) return QImage();
QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
return image;
}
QImage KisImage::convertToQImage(const QRect& scaledRect, const QSize& scaledImageSize, const KoColorProfile *profile)
{
if (scaledRect.isEmpty() || scaledImageSize.isEmpty()) {
return QImage();
}
try {
qint32 imageWidth = width();
qint32 imageHeight = height();
quint32 pixelSize = colorSpace()->pixelSize();
double xScale = static_cast<double>(imageWidth) / scaledImageSize.width();
double yScale = static_cast<double>(imageHeight) / scaledImageSize.height();
QRect srcRect;
srcRect.setLeft(static_cast<int>(scaledRect.left() * xScale));
srcRect.setRight(static_cast<int>(ceil((scaledRect.right() + 1) * xScale)) - 1);
srcRect.setTop(static_cast<int>(scaledRect.top() * yScale));
srcRect.setBottom(static_cast<int>(ceil((scaledRect.bottom() + 1) * yScale)) - 1);
KisPaintDeviceSP mergedImage = projection();
quint8 *scaledImageData = new quint8[scaledRect.width() * scaledRect.height() * pixelSize];
quint8 *imageRow = new quint8[srcRect.width() * pixelSize];
const qint32 imageRowX = srcRect.x();
for (qint32 y = 0; y < scaledRect.height(); ++y) {
qint32 dstY = scaledRect.y() + y;
qint32 dstX = scaledRect.x();
qint32 srcY = (dstY * imageHeight) / scaledImageSize.height();
mergedImage->readBytes(imageRow, imageRowX, srcY, srcRect.width(), 1);
quint8 *dstPixel = scaledImageData + (y * scaledRect.width() * pixelSize);
quint32 columnsRemaining = scaledRect.width();
while (columnsRemaining > 0) {
qint32 srcX = (dstX * imageWidth) / scaledImageSize.width();
memcpy(dstPixel, imageRow + ((srcX - imageRowX) * pixelSize), pixelSize);
++dstX;
dstPixel += pixelSize;
--columnsRemaining;
}
}
delete [] imageRow;
QImage image = colorSpace()->convertToQImage(scaledImageData, scaledRect.width(), scaledRect.height(), const_cast<KoColorProfile*>(profile),
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
delete [] scaledImageData;
return image;
}
catch (std::bad_alloc) {
warnKrita << "KisImage::convertToQImage ran out of memory";
return QImage();
}
}
void KisImage::notifyLayersChanged()
{
m_d->signalRouter->emitNotification(LayersChangedSignal);
}
QRect KisImage::bounds() const
{
return QRect(0, 0, width(), height());
}
KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
{
return m_d->postExecutionUndoAdapter;
}
void KisImage::setUndoStore(KisUndoStore *undoStore)
{
m_d->legacyUndoAdapter->setUndoStore(undoStore);
m_d->postExecutionUndoAdapter->setUndoStore(undoStore);
delete m_d->undoStore;
m_d->undoStore = undoStore;
}
KisUndoStore* KisImage::undoStore()
{
return m_d->undoStore;
}
KisUndoAdapter* KisImage::undoAdapter() const
{
return m_d->legacyUndoAdapter;
}
KisActionRecorder* KisImage::actionRecorder() const
{
return m_d->recorder;
}
void KisImage::setDefaultProjectionColor(const KoColor &color)
{
KIS_ASSERT_RECOVER_RETURN(m_d->rootLayer);
m_d->rootLayer->setDefaultProjectionColor(color);
}
KoColor KisImage::defaultProjectionColor() const
{
KIS_ASSERT_RECOVER(m_d->rootLayer) {
return KoColor(Qt::transparent, m_d->colorSpace);
}
return m_d->rootLayer->defaultProjectionColor();
}
void KisImage::setRootLayer(KisGroupLayerSP rootLayer)
{
stopIsolatedMode();
KoColor defaultProjectionColor(Qt::transparent, m_d->colorSpace);
if (m_d->rootLayer) {
m_d->rootLayer->setGraphListener(0);
m_d->rootLayer->disconnect();
KisPaintDeviceSP original = m_d->rootLayer->original();
defaultProjectionColor.setColor(original->defaultPixel(), original->colorSpace());
}
m_d->rootLayer = rootLayer;
m_d->rootLayer->disconnect();
m_d->rootLayer->setGraphListener(this);
KisPaintDeviceSP newOriginal = m_d->rootLayer->original();
defaultProjectionColor.convertTo(newOriginal->colorSpace());
newOriginal->setDefaultPixel(defaultProjectionColor.data());
setRoot(m_d->rootLayer.data());
}
void KisImage::addAnnotation(KisAnnotationSP annotation)
{
// Find the icc annotation, if there is one
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == annotation->type()) {
*it = annotation;
return;
}
++it;
}
m_d->annotations.push_back(annotation);
}
KisAnnotationSP KisImage::annotation(const QString& type)
{
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == type) {
return *it;
}
++it;
}
return KisAnnotationSP(0);
}
void KisImage::removeAnnotation(const QString& type)
{
vKisAnnotationSP_it it = m_d->annotations.begin();
while (it != m_d->annotations.end()) {
if ((*it)->type() == type) {
m_d->annotations.erase(it);
return;
}
++it;
}
}
vKisAnnotationSP_it KisImage::beginAnnotations()
{
return m_d->annotations.begin();
}
vKisAnnotationSP_it KisImage::endAnnotations()
{
return m_d->annotations.end();
}
void KisImage::notifyAboutToBeDeleted()
{
emit sigAboutToBeDeleted();
}
KisPerspectiveGrid* KisImage::perspectiveGrid()
{
if (m_d->perspectiveGrid == 0)
m_d->perspectiveGrid = new KisPerspectiveGrid();
return m_d->perspectiveGrid;
}
KisImageSignalRouter* KisImage::signalRouter()
{
return m_d->signalRouter;
}
void KisImage::waitForDone()
{
requestStrokeEnd();
if (m_d->scheduler) {
m_d->scheduler->waitForDone();
}
}
KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy)
{
/**
* Ask open strokes to end gracefully. All the strokes clients
* (including the one calling this method right now) will get
* a notification that they should probably end their strokes.
* However this is purely their choice whether to end a stroke
* or not.
*/
requestStrokeEnd();
/**
* Some of the strokes can cancel their work with undoing all the
* changes they did to the paint devices. The problem is that undo
* stack will know nothing about it. Therefore, just notify it
* explicitly
*/
if (strokeStrategy->clearsRedoOnStart()) {
m_d->undoStore->purgeRedoState();
}
KisStrokeId id;
if (m_d->scheduler) {
id = m_d->scheduler->startStroke(strokeStrategy);
}
return id;
}
void KisImage::KisImagePrivate::notifyProjectionUpdatedInPatches(const QRect &rc)
{
KisImageConfig imageConfig;
int patchWidth = imageConfig.updatePatchWidth();
int patchHeight = imageConfig.updatePatchHeight();
for (int y = 0; y < rc.height(); y += patchHeight) {
for (int x = 0; x < rc.width(); x += patchWidth) {
QRect patchRect(x, y, patchWidth, patchHeight);
patchRect &= rc;
QtConcurrent::run(boost::bind(&KisImage::notifyProjectionUpdated, q, patchRect));
}
}
}
bool KisImage::startIsolatedMode(KisNodeSP node)
{
if (!tryBarrierLock()) return false;
unlock();
m_d->isolatedRootNode = node;
emit sigIsolatedModeChanged();
// the GUI uses our thread to do the color space conversion so we
// need to emit this signal in multiple threads
m_d->notifyProjectionUpdatedInPatches(bounds());
return true;
}
void KisImage::stopIsolatedMode()
{
if (!m_d->isolatedRootNode) return;
KisNodeSP oldRootNode = m_d->isolatedRootNode;
m_d->isolatedRootNode = 0;
emit sigIsolatedModeChanged();
// the GUI uses our thread to do the color space conversion so we
// need to emit this signal in multiple threads
m_d->notifyProjectionUpdatedInPatches(bounds());
// TODO: Substitute notifyProjectionUpdated() with this code
// when update optimization is implemented
//
// QRect updateRect = bounds() | oldRootNode->extent();
// oldRootNode->setDirty(updateRect);
}
KisNodeSP KisImage::isolatedModeRoot() const
{
return m_d->isolatedRootNode;
}
void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data)
{
KisUpdateTimeMonitor::instance()->reportJobStarted(data);
if (m_d->scheduler) {
m_d->scheduler->addJob(id, data);
}
}
void KisImage::endStroke(KisStrokeId id)
{
if (m_d->scheduler) {
m_d->scheduler->endStroke(id);
}
}
bool KisImage::cancelStroke(KisStrokeId id)
{
bool result = false;
if (m_d->scheduler) {
result = m_d->scheduler->cancelStroke(id);
}
return result;
}
bool KisImage::KisImagePrivate::tryCancelCurrentStrokeAsync()
{
bool result = false;
if (scheduler) {
result = scheduler->tryCancelCurrentStrokeAsync();
}
return result;
}
void KisImage::requestUndoDuringStroke()
{
emit sigUndoDuringStrokeRequested();
}
void KisImage::requestStrokeCancellation()
{
if (!m_d->tryCancelCurrentStrokeAsync()) {
emit sigStrokeCancellationRequested();
}
}
void KisImage::requestStrokeEnd()
{
emit sigStrokeEndRequested();
}
void KisImage::refreshGraph(KisNodeSP root)
{
refreshGraph(root, bounds(), bounds());
}
void KisImage::refreshGraph(KisNodeSP root, const QRect &rc, const QRect &cropRect)
{
if (!root) root = m_d->rootLayer;
if (m_d->scheduler) {
m_d->scheduler->fullRefresh(root, rc, cropRect);
}
}
void KisImage::initialRefreshGraph()
{
/**
* NOTE: Tricky part. We set crop rect to null, so the clones
* will not rely on precalculated projections of their sources
*/
refreshGraphAsync(0, bounds(), QRect());
waitForDone();
}
void KisImage::refreshGraphAsync(KisNodeSP root)
{
refreshGraphAsync(root, bounds(), bounds());
}
void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc)
{
refreshGraphAsync(root, rc, bounds());
}
void KisImage::refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect)
{
if (!root) root = m_d->rootLayer;
if (m_d->scheduler) {
m_d->scheduler->fullRefreshAsync(root, rc, cropRect);
}
}
void KisImage::requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect)
{
KIS_ASSERT_RECOVER_RETURN(pseudoFilthy);
if (m_d->scheduler) {
m_d->scheduler->updateProjectionNoFilthy(pseudoFilthy, rc, cropRect);
}
}
void KisImage::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
if (m_d->scheduler) {
m_d->scheduler->addSpontaneousJob(spontaneousJob);
}
}
void KisImage::disableDirtyRequests()
{
m_d->disableDirtyRequests.ref();
}
void KisImage::enableDirtyRequests()
{
m_d->disableDirtyRequests.deref();
}
void KisImage::disableUIUpdates()
{
m_d->disableUIUpdateSignals.ref();
}
void KisImage::enableUIUpdates()
{
m_d->disableUIUpdateSignals.deref();
}
void KisImage::notifyProjectionUpdated(const QRect &rc)
{
KisUpdateTimeMonitor::instance()->reportUpdateFinished(rc);
if (!m_d->disableUIUpdateSignals) {
emit sigImageUpdated(rc);
}
}
void KisImage::notifySelectionChanged()
{
/**
* The selection is calculated asynchromously, so it is not
* handled by disableUIUpdates() and other special signals of
* KisImageSignalRouter
*/
m_d->legacyUndoAdapter->emitSelectionChanged();
/**
* Editing of selection masks doesn't necessary produce a
* setDirty() call, so in the end of the stroke we need to request
* direct update of the UI's cache.
*/
if (m_d->isolatedRootNode &&
dynamic_cast<KisSelectionMask*>(m_d->isolatedRootNode.data())) {
notifyProjectionUpdated(bounds());
}
}
void KisImage::requestProjectionUpdateImpl(KisNode *node,
const QRect &rect,
const QRect &cropRect)
{
KisNodeGraphListener::requestProjectionUpdate(node, rect);
if (m_d->scheduler) {
m_d->scheduler->updateProjection(node, rect, cropRect);
}
}
void KisImage::requestProjectionUpdate(KisNode *node, const QRect& rect)
{
if (m_d->disableDirtyRequests) return;
/**
* Here we use 'permitted' instead of 'active' intentively,
* because the updates may come after the actual stroke has been
* finished. And having some more updates for the stroke not
* supporting the wrap-around mode will not make much harm.
*/
if (m_d->wrapAroundModePermitted) {
QRect boundRect = bounds();
KisWrappedRect splitRect(rect, boundRect);
foreach (const QRect &rc, splitRect) {
requestProjectionUpdateImpl(node, rc, boundRect);
}
} else {
requestProjectionUpdateImpl(node, rect, bounds());
}
}
QList<KisLayerComposition*> KisImage::compositions()
{
return m_d->compositions;
}
void KisImage::addComposition(KisLayerComposition* composition)
{
m_d->compositions.append(composition);
}
void KisImage::removeComposition(KisLayerComposition* composition)
{
m_d->compositions.removeAll(composition);
delete composition;
}
bool checkMasksNeedConversion(KisNodeSP root, const QRect &bounds)
{
KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(root.data());
if (mask &&
(!bounds.contains(mask->paintDevice()->exactBounds()) ||
mask->selection()->hasShapeSelection())) {
return true;
}
KisNodeSP node = root->firstChild();
while (node) {
if (checkMasksNeedConversion(node, bounds)) {
return true;
}
node = node->nextSibling();
}
return false;
}
void KisImage::setWrapAroundModePermitted(bool value)
{
m_d->wrapAroundModePermitted = value;
if (m_d->wrapAroundModePermitted &&
checkMasksNeedConversion(root(), bounds())) {
KisProcessingApplicator applicator(this, root(),
KisProcessingApplicator::RECURSIVE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Crop Selections"));
KisProcessingVisitorSP visitor =
new KisCropSelectionsProcessingVisitor(bounds());
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
}
bool KisImage::wrapAroundModePermitted() const
{
return m_d->wrapAroundModePermitted;
}
bool KisImage::wrapAroundModeActive() const
{
return m_d->wrapAroundModePermitted && m_d->scheduler &&
m_d->scheduler->wrapAroundModeSupported();
}
void KisImage::notifyNodeCollpasedChanged()
{
emit sigNodeCollapsedChanged();
}
diff --git a/krita/image/kis_image_config.cpp b/krita/image/kis_image_config.cpp
index 846100e4fd7..56495d4400d 100644
--- a/krita/image/kis_image_config.cpp
+++ b/krita/image/kis_image_config.cpp
@@ -1,288 +1,288 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_image_config.h"
#include <ksharedconfig.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <KoConfig.h>
#include "kis_debug.h"
#include <QThread>
#include <QApplication>
KisImageConfig::KisImageConfig()
: m_config(KGlobal::config()->group(""))
{
}
KisImageConfig::~KisImageConfig()
{
if (qApp->thread() != QThread::currentThread()) {
- qDebug() << "WARNING: KisImageConfig: requested config synchronization from nonGUI thread! Skipping...";
+ dbgKrita << "WARNING: KisImageConfig: requested config synchronization from nonGUI thread! Skipping...";
return;
}
m_config.sync();
}
bool KisImageConfig::enableProgressReporting(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("enableProgressReporting", true) : true;
}
void KisImageConfig::setEnableProgressReporting(bool value)
{
m_config.writeEntry("enableProgressReporting", value);
}
bool KisImageConfig::enablePerfLog(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("enablePerfLog", false) :false;
}
void KisImageConfig::setEnablePerfLog(bool value)
{
m_config.writeEntry("enablePerfLog", value);
}
qreal KisImageConfig::transformMaskOffBoundsReadArea() const
{
return m_config.readEntry("transformMaskOffBoundsReadArea", 0.5);
}
int KisImageConfig::updatePatchHeight() const
{
return m_config.readEntry("updatePatchHeight", 512);
}
void KisImageConfig::setUpdatePatchHeight(int value)
{
m_config.writeEntry("updatePatchHeight", value);
}
int KisImageConfig::updatePatchWidth() const
{
return m_config.readEntry("updatePatchWidth", 512);
}
void KisImageConfig::setUpdatePatchWidth(int value)
{
m_config.writeEntry("updatePatchWidth", value);
}
qreal KisImageConfig::maxCollectAlpha() const
{
return m_config.readEntry("maxCollectAlpha", 2.5);
}
qreal KisImageConfig::maxMergeAlpha() const
{
return m_config.readEntry("maxMergeAlpha", 1.);
}
qreal KisImageConfig::maxMergeCollectAlpha() const
{
return m_config.readEntry("maxMergeCollectAlpha", 1.5);
}
qreal KisImageConfig::schedulerBalancingRatio() const
{
/**
* updates-queue-size / strokes-queue-size
*/
return m_config.readEntry("schedulerBalancingRatio", 100.);
}
void KisImageConfig::setSchedulerBalancingRatio(qreal value)
{
m_config.writeEntry("schedulerBalancingRatio", value);
}
int KisImageConfig::maxSwapSize(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("maxSwapSize", 4096) : 4096; // in MiB
}
void KisImageConfig::setMaxSwapSize(int value)
{
m_config.writeEntry("maxSwapSize", value);
}
int KisImageConfig::swapSlabSize() const
{
return m_config.readEntry("swapSlabSize", 64); // in MiB
}
void KisImageConfig::setSwapSlabSize(int value)
{
m_config.writeEntry("swapSlabSize", value);
}
int KisImageConfig::swapWindowSize() const
{
return m_config.readEntry("swapWindowSize", 16); // in MiB
}
void KisImageConfig::setSwapWindowSize(int value)
{
m_config.writeEntry("swapWindowSize", value);
}
int KisImageConfig::tilesHardLimit() const
{
qreal hp = qreal(memoryHardLimitPercent()) / 100.0;
qreal pp = qreal(memoryPoolLimitPercent()) / 100.0;
return totalRAM() * hp * (1 - pp);
}
int KisImageConfig::tilesSoftLimit() const
{
qreal sp = qreal(memorySoftLimitPercent()) / 100.0;
return tilesHardLimit() * sp;
}
int KisImageConfig::poolLimit() const
{
qreal hp = qreal(memoryHardLimitPercent()) / 100.0;
qreal pp = qreal(memoryPoolLimitPercent()) / 100.0;
return totalRAM() * hp * pp;
}
qreal KisImageConfig::memoryHardLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memoryHardLimitPercent", 50.) : 50.;
}
void KisImageConfig::setMemoryHardLimitPercent(qreal value)
{
m_config.writeEntry("memoryHardLimitPercent", value);
}
qreal KisImageConfig::memorySoftLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memorySoftLimitPercent", 2.) : 2.;
}
void KisImageConfig::setMemorySoftLimitPercent(qreal value)
{
m_config.writeEntry("memorySoftLimitPercent", value);
}
qreal KisImageConfig::memoryPoolLimitPercent(bool requestDefault) const
{
return !requestDefault ?
m_config.readEntry("memoryPoolLimitPercent", 2.) : 2.;
}
void KisImageConfig::setMemoryPoolLimitPercent(qreal value)
{
m_config.writeEntry("memoryPoolLimitPercent", value);
}
QString KisImageConfig::swapDir(bool requestDefault)
{
QString swap = KGlobal::dirs()->locateLocal("tmp", "krita");
return !requestDefault ?
m_config.readEntry("swaplocation", swap) : swap;
}
void KisImageConfig::setSwapDir(const QString &swapDir)
{
m_config.writeEntry("swaplocation", swapDir);
}
#if defined Q_OS_LINUX
#include <sys/sysinfo.h>
#elif defined Q_OS_FREEBSD
#include <sys/sysctl.h>
#elif defined Q_OS_WIN
#include <windows.h>
#elif defined Q_OS_MAC64
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
-#include <kdebug.h>
+#include <kis_debug.h>
int KisImageConfig::totalRAM()
{
// let's think that default memory size is 1000MiB
int totalMemory = 1000; // MiB
int error = 1;
#if defined Q_OS_LINUX
struct sysinfo info;
error = sysinfo(&info);
if(!error) {
totalMemory = info.totalram * info.mem_unit / (1UL << 20);
}
#elif defined Q_OS_FREEBSD
u_long physmem;
int mib[] = {CTL_HW, HW_PHYSMEM};
size_t len = sizeof(physmem);
error = sysctl(mib, 2, &physmem, &len, NULL, 0);
if(!error) {
totalMemory = physmem >> 20;
}
#elif defined Q_OS_WIN
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
error = !GlobalMemoryStatusEx(&status);
if (!error) {
totalMemory = status.ullTotalPhys >> 20;
}
// For 32 bit windows, the total memory available is at max the 2GB per process memory limit.
# if defined ENV32BIT
totalMemory = qMin(totalMemory, 2000);
# endif
#elif defined Q_OS_MAC64
int mib[2] = { CTL_HW, HW_MEMSIZE };
u_int namelen = sizeof(mib) / sizeof(mib[0]);
uint64_t size;
size_t len = sizeof(size);
if (sysctl(mib, namelen, &size, &len, NULL, 0) > 0) {
totalMemory = size;
error = 0;
}
#endif
if(error) {
- kWarning() << "Cannot get the size of your RAM."
+ dbgKrita << "Cannot get the size of your RAM."
<< "Using default value of 1GiB.";
}
return totalMemory;
}
diff --git a/krita/image/kis_layer_composition.cpp b/krita/image/kis_layer_composition.cpp
index 8354139bf22..435f57c8875 100644
--- a/krita/image/kis_layer_composition.cpp
+++ b/krita/image/kis_layer_composition.cpp
@@ -1,167 +1,167 @@
/*
* Copyright (c) 2012 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_composition.h"
#include "kis_node_visitor.h"
#include "kis_group_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_external_layer_iface.h"
#include "kis_paint_layer.h"
#include "generator/kis_generator_layer.h"
#include "kis_clone_layer.h"
#include "kis_filter_mask.h"
#include "kis_transform_mask.h"
#include "kis_transparency_mask.h"
#include "kis_selection_mask.h"
#include <QDomDocument>
class KisCompositionVisitor : public KisNodeVisitor {
public:
enum Mode {
STORE,
APPLY
};
KisCompositionVisitor(KisLayerComposition* layerComposition, Mode mode) : m_layerComposition(layerComposition), m_mode(mode)
{
}
virtual bool visit(KisNode* node) { return process(node); }
virtual bool visit(KisGroupLayer* layer)
{
bool result = visitAll(layer);
if(layer == layer->image()->rootLayer()) {
return result;
}
return result && process(layer);
}
virtual bool visit(KisAdjustmentLayer* layer) { return process(layer); }
virtual bool visit(KisPaintLayer* layer) { return process(layer); }
virtual bool visit(KisExternalLayer* layer) { return process(layer); }
virtual bool visit(KisGeneratorLayer* layer) { return process(layer); }
virtual bool visit(KisCloneLayer* layer) { return process(layer); }
virtual bool visit(KisFilterMask* mask) { return process(mask); }
virtual bool visit(KisTransformMask* mask) { return process(mask); }
virtual bool visit(KisTransparencyMask* mask) { return process(mask); }
virtual bool visit(KisSelectionMask* mask) { return process(mask); }
bool process(KisNode* node) {
if(m_mode == STORE) {
m_layerComposition->m_visibilityMap[node->uuid()] = node->visible();
m_layerComposition->m_collapsedMap[node->uuid()] = node->collapsed();
} else {
bool newState = false;
if(m_layerComposition->m_visibilityMap.contains(node->uuid())) {
newState = m_layerComposition->m_visibilityMap[node->uuid()];
}
if(node->visible() != newState) {
node->setVisible(m_layerComposition->m_visibilityMap[node->uuid()]);
node->setDirty();
}
if(m_layerComposition->m_collapsedMap.contains(node->uuid())) {
node->setCollapsed(m_layerComposition->m_collapsedMap[node->uuid()]);
}
}
return true;
}
private:
KisLayerComposition* m_layerComposition;
Mode m_mode;
};
KisLayerComposition::KisLayerComposition(KisImageWSP image, const QString& name): m_image(image), m_name(name), m_exportEnabled(true)
{
}
KisLayerComposition::~KisLayerComposition()
{
}
void KisLayerComposition::setName(const QString& name)
{
m_name = name;
}
QString KisLayerComposition::name()
{
return m_name;
}
void KisLayerComposition::store()
{
if(m_image.isNull()) {
return;
}
KisCompositionVisitor visitor(this, KisCompositionVisitor::STORE);
m_image->rootLayer()->accept(visitor);
}
void KisLayerComposition::apply()
{
if(m_image.isNull()) {
return;
}
KisCompositionVisitor visitor(this, KisCompositionVisitor::APPLY);
m_image->rootLayer()->accept(visitor);
m_image->notifyNodeCollpasedChanged();
}
void KisLayerComposition::setExportEnabled ( bool enabled )
{
m_exportEnabled = enabled;
}
bool KisLayerComposition::isExportEnabled()
{
return m_exportEnabled;
}
void KisLayerComposition::setVisible(QUuid id, bool visible)
{
m_visibilityMap[id] = visible;
}
void KisLayerComposition::setCollapsed ( QUuid id, bool collapsed )
{
m_collapsedMap[id] = collapsed;
}
void KisLayerComposition::save(QDomDocument& doc, QDomElement& element)
{
QDomElement compositionElement = doc.createElement("composition");
compositionElement.setAttribute("name", m_name);
compositionElement.setAttribute("exportEnabled", m_exportEnabled);
QMapIterator<QUuid, bool> iter(m_visibilityMap);
while (iter.hasNext()) {
iter.next();
QDomElement valueElement = doc.createElement("value");
valueElement.setAttribute("uuid", iter.key().toString());
valueElement.setAttribute("visible", iter.value());
- kDebug() << "contains" << m_collapsedMap.contains(iter.key());
+ dbgKrita << "contains" << m_collapsedMap.contains(iter.key());
if (m_collapsedMap.contains(iter.key())) {
- kDebug() << "colapsed :" << m_collapsedMap[iter.key()];
+ dbgKrita << "colapsed :" << m_collapsedMap[iter.key()];
valueElement.setAttribute("collapsed", m_collapsedMap[iter.key()]);
}
compositionElement.appendChild(valueElement);
}
element.appendChild(compositionElement);
}
diff --git a/krita/image/kis_liquify_transform_worker.cpp b/krita/image/kis_liquify_transform_worker.cpp
index 0e5e7ee8ae4..e07cd452ad3 100644
--- a/krita/image/kis_liquify_transform_worker.cpp
+++ b/krita/image/kis_liquify_transform_worker.cpp
@@ -1,562 +1,562 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_liquify_transform_worker.h"
#include "kis_grid_interpolation_tools.h"
#include "kis_dom_utils.h"
struct Q_DECL_HIDDEN KisLiquifyTransformWorker::Private
{
Private(const QRect &_srcBounds,
KoUpdater *_progress,
int _pixelPrecision)
: srcBounds(_srcBounds),
progress(_progress),
pixelPrecision(_pixelPrecision)
{
}
const QRect srcBounds;
QVector<QPointF> originalPoints;
QVector<QPointF> transformedPoints;
KoUpdater *progress;
int pixelPrecision;
QSize gridSize;
void preparePoints();
struct MapIndexesOp;
template <class ProcessOp>
void processTransformedPixelsBuildUp(ProcessOp op,
const QPointF &base,
qreal sigma);
template <class ProcessOp>
void processTransformedPixelsWash(ProcessOp op,
const QPointF &base,
qreal sigma,
qreal flow);
template <class ProcessOp>
void processTransformedPixels(ProcessOp op,
const QPointF &base,
qreal sigma,
bool useWashMode,
qreal flow);
};
KisLiquifyTransformWorker::KisLiquifyTransformWorker(const QRect &srcBounds,
KoUpdater *progress,
int pixelPrecision)
: m_d(new Private(srcBounds, progress, pixelPrecision))
{
KIS_ASSERT_RECOVER_RETURN(!srcBounds.isEmpty());
// TODO: implement 'progress' stuff
m_d->preparePoints();
}
KisLiquifyTransformWorker::KisLiquifyTransformWorker(const KisLiquifyTransformWorker &rhs)
: m_d(new Private(*rhs.m_d.data()))
{
}
KisLiquifyTransformWorker::~KisLiquifyTransformWorker()
{
}
bool KisLiquifyTransformWorker::operator==(const KisLiquifyTransformWorker &other) const
{
return
m_d->srcBounds == other.m_d->srcBounds &&
m_d->originalPoints == other.m_d->originalPoints &&
m_d->transformedPoints == other.m_d->transformedPoints &&
m_d->pixelPrecision == other.m_d->pixelPrecision &&
m_d->gridSize == other.m_d->gridSize;
}
int KisLiquifyTransformWorker::pointToIndex(const QPoint &cellPt)
{
return GridIterationTools::pointToIndex(cellPt, m_d->gridSize);
}
QSize KisLiquifyTransformWorker::gridSize() const
{
return m_d->gridSize;
}
const QVector<QPointF>& KisLiquifyTransformWorker::originalPoints() const
{
return m_d->originalPoints;
}
QVector<QPointF>& KisLiquifyTransformWorker::transformedPoints()
{
return m_d->transformedPoints;
}
struct AllPointsFetcherOp
{
AllPointsFetcherOp(QRectF srcRect) : m_srcRect(srcRect) {}
inline void processPoint(int col, int row,
int prevCol, int prevRow,
int colIndex, int rowIndex) {
Q_UNUSED(prevCol);
Q_UNUSED(prevRow);
Q_UNUSED(colIndex);
Q_UNUSED(rowIndex);
QPointF pt(col, row);
m_points << pt;
}
inline void nextLine() {
}
QVector<QPointF> m_points;
QRectF m_srcRect;
};
void KisLiquifyTransformWorker::Private::preparePoints()
{
gridSize =
GridIterationTools::calcGridSize(srcBounds, pixelPrecision);
AllPointsFetcherOp pointsOp(srcBounds);
GridIterationTools::processGrid(pointsOp, srcBounds, pixelPrecision);
const int numPoints = pointsOp.m_points.size();
KIS_ASSERT_RECOVER_RETURN(numPoints == gridSize.width() * gridSize.height());
originalPoints = pointsOp.m_points;
transformedPoints = pointsOp.m_points;
}
void KisLiquifyTransformWorker::translate(const QPointF &offset)
{
QVector<QPointF>::iterator it = m_d->transformedPoints.begin();
QVector<QPointF>::iterator end = m_d->transformedPoints.end();
QVector<QPointF>::iterator refIt = m_d->originalPoints.begin();
KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() ==
m_d->transformedPoints.size());
for (; it != end; ++it, ++refIt) {
*it += offset;
*refIt += offset;
}
}
void KisLiquifyTransformWorker::undoPoints(const QPointF &base,
qreal amount,
qreal sigma)
{
const qreal maxDistCoeff = 3.0;
const qreal maxDist = maxDistCoeff * sigma;
QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
2 * maxDist, 2 * maxDist);
QVector<QPointF>::iterator it = m_d->transformedPoints.begin();
QVector<QPointF>::iterator end = m_d->transformedPoints.end();
QVector<QPointF>::iterator refIt = m_d->originalPoints.begin();
KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() ==
m_d->transformedPoints.size());
for (; it != end; ++it, ++refIt) {
if (!clipRect.contains(*it)) continue;
QPointF diff = *it - base;
qreal dist = KisAlgebra2D::norm(diff);
if (dist > maxDist) continue;
qreal lambda = exp(-0.5 * pow2(dist / sigma));
lambda *= amount;
*it = *refIt * lambda + *it * (1.0 - lambda);
}
}
template <class ProcessOp>
void KisLiquifyTransformWorker::Private::
processTransformedPixelsBuildUp(ProcessOp op,
const QPointF &base,
qreal sigma)
{
const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
2 * maxDist, 2 * maxDist);
QVector<QPointF>::iterator it = transformedPoints.begin();
QVector<QPointF>::iterator end = transformedPoints.end();
for (; it != end; ++it) {
if (!clipRect.contains(*it)) continue;
QPointF diff = *it - base;
qreal dist = KisAlgebra2D::norm(diff);
if (dist > maxDist) continue;
const qreal lambda = exp(-0.5 * pow2(dist / sigma));
*it = op(*it, base, diff, lambda);
}
}
template <class ProcessOp>
void KisLiquifyTransformWorker::Private::
processTransformedPixelsWash(ProcessOp op,
const QPointF &base,
qreal sigma,
qreal flow)
{
const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
2 * maxDist, 2 * maxDist);
QVector<QPointF>::iterator it = transformedPoints.begin();
QVector<QPointF>::iterator end = transformedPoints.end();
QVector<QPointF>::iterator refIt = originalPoints.begin();
KIS_ASSERT_RECOVER_RETURN(originalPoints.size() ==
transformedPoints.size());
for (; it != end; ++it, ++refIt) {
if (!clipRect.contains(*it)) continue;
QPointF diff = *refIt - base;
qreal dist = KisAlgebra2D::norm(diff);
if (dist > maxDist) continue;
const qreal lambda = exp(-0.5 * pow2(dist / sigma));
QPointF dstPt = op(*refIt, base, diff, lambda);
if (kisDistance(dstPt, *refIt) > kisDistance(*it, *refIt)) {
*it = (1.0 - flow) * (*it) + flow * dstPt;
}
}
}
template <class ProcessOp>
void KisLiquifyTransformWorker::Private::
processTransformedPixels(ProcessOp op,
const QPointF &base,
qreal sigma,
bool useWashMode,
qreal flow)
{
if (useWashMode) {
processTransformedPixelsWash(op, base, sigma, flow);
} else {
processTransformedPixelsBuildUp(op, base, sigma);
}
}
struct TranslateOp
{
TranslateOp(const QPointF &offset) : m_offset(offset) {}
QPointF operator() (const QPointF &pt,
const QPointF &base,
const QPointF &diff,
qreal lambda)
{
Q_UNUSED(base);
Q_UNUSED(diff);
return pt + lambda * m_offset;
}
static const qreal maxDistCoeff;
QPointF m_offset;
};
const qreal TranslateOp::maxDistCoeff = 3.0;
struct ScaleOp
{
ScaleOp(qreal scale) : m_scale(scale) {}
QPointF operator() (const QPointF &pt,
const QPointF &base,
const QPointF &diff,
qreal lambda)
{
Q_UNUSED(pt);
Q_UNUSED(diff);
return base + (1.0 + m_scale * lambda) * diff;
}
static const qreal maxDistCoeff;
qreal m_scale;
};
const qreal ScaleOp::maxDistCoeff = 3.0;
struct RotateOp
{
RotateOp(qreal angle) : m_angle(angle) {}
QPointF operator() (const QPointF &pt,
const QPointF &base,
const QPointF &diff,
qreal lambda)
{
Q_UNUSED(pt);
const qreal angle = m_angle * lambda;
const qreal sinA = std::sin(angle);
const qreal cosA = std::cos(angle);
qreal x = cosA * diff.x() + sinA * diff.y();
qreal y = -sinA * diff.x() + cosA * diff.y();
return base + QPointF(x, y);
}
static const qreal maxDistCoeff;
qreal m_angle;
};
const qreal RotateOp::maxDistCoeff = 3.0;
void KisLiquifyTransformWorker::translatePoints(const QPointF &base,
const QPointF &offset,
qreal sigma,
bool useWashMode,
qreal flow)
{
TranslateOp op(offset);
m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
}
void KisLiquifyTransformWorker::scalePoints(const QPointF &base,
qreal scale,
qreal sigma,
bool useWashMode,
qreal flow)
{
ScaleOp op(scale);
m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
}
void KisLiquifyTransformWorker::rotatePoints(const QPointF &base,
qreal angle,
qreal sigma,
bool useWashMode,
qreal flow)
{
RotateOp op(angle);
m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
}
struct KisLiquifyTransformWorker::Private::MapIndexesOp {
MapIndexesOp(KisLiquifyTransformWorker::Private *d)
: m_d(d)
{
}
inline QVector<int> calculateMappedIndexes(int col, int row,
int *numExistingPoints) const {
*numExistingPoints = 4;
QVector<int> cellIndexes =
GridIterationTools::calculateCellIndexes(col, row, m_d->gridSize);
return cellIndexes;
}
inline int tryGetValidIndex(const QPoint &cellPt) const {
Q_UNUSED(cellPt);
KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
return -1;
}
inline QPointF getSrcPointForce(const QPoint &cellPt) const {
Q_UNUSED(cellPt);
KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
return QPointF();
}
inline const QPolygonF srcCropPolygon() const {
KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
return QPolygonF();
}
KisLiquifyTransformWorker::Private *m_d;
};
void KisLiquifyTransformWorker::run(KisPaintDeviceSP device)
{
KisPaintDeviceSP srcDev = new KisPaintDevice(*device.data());
device->clear();
using namespace GridIterationTools;
PaintDevicePolygonOp polygonOp(srcDev, device);
Private::MapIndexesOp indexesOp(m_d.data());
iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
m_d->originalPoints,
m_d->transformedPoints);
}
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <QTransform>
typedef boost::function<QPointF (const QPointF&)> PointMapFunction;
PointMapFunction bindPointMapTransform(const QTransform &transform) {
typedef QPointF (QTransform::*MapFuncType)(const QPointF&) const;
return boost::bind(static_cast<MapFuncType>(&QTransform::map), &transform, _1);
}
QImage KisLiquifyTransformWorker::runOnQImage(const QImage &srcImage,
const QPointF &srcImageOffset,
const QTransform &imageToThumbTransform,
QPointF *newOffset)
{
KIS_ASSERT_RECOVER(m_d->originalPoints.size() == m_d->transformedPoints.size()) {
return QImage();
}
KIS_ASSERT_RECOVER(!srcImage.isNull()) {
return QImage();
}
KIS_ASSERT_RECOVER(srcImage.format() == QImage::Format_ARGB32) {
return QImage();
}
QVector<QPointF> originalPointsLocal(m_d->originalPoints);
QVector<QPointF> transformedPointsLocal(m_d->transformedPoints);
PointMapFunction mapFunc = bindPointMapTransform(imageToThumbTransform);
std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
originalPointsLocal.begin(), mapFunc);
std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
transformedPointsLocal.begin(), mapFunc);
QRectF dstBounds;
foreach (const QPointF &pt, transformedPointsLocal) {
KisAlgebra2D::accumulateBounds(pt, &dstBounds);
}
const QRectF srcBounds(srcImageOffset, srcImage.size());
dstBounds |= srcBounds;
QPointF dstQImageOffset = dstBounds.topLeft();
*newOffset = dstQImageOffset;
QRect dstBoundsI = dstBounds.toAlignedRect();
QImage dstImage(dstBoundsI.size(), srcImage.format());
dstImage.fill(0);
GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcImageOffset, dstQImageOffset);
Private::MapIndexesOp indexesOp(m_d.data());
GridIterationTools::iterateThroughGrid
<GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
originalPointsLocal,
transformedPointsLocal);
return dstImage;
}
void KisLiquifyTransformWorker::toXML(QDomElement *e) const
{
QDomDocument doc = e->ownerDocument();
QDomElement liqEl = doc.createElement("liquify_points");
e->appendChild(liqEl);
KisDomUtils::saveValue(&liqEl, "srcBounds", m_d->srcBounds);
KisDomUtils::saveValue(&liqEl, "originalPoints", m_d->originalPoints);
KisDomUtils::saveValue(&liqEl, "transformedPoints", m_d->transformedPoints);
KisDomUtils::saveValue(&liqEl, "pixelPrecision", m_d->pixelPrecision);
KisDomUtils::saveValue(&liqEl, "gridSize", m_d->gridSize);
}
KisLiquifyTransformWorker* KisLiquifyTransformWorker::fromXML(const QDomElement &e)
{
QDomElement liquifyEl;
QRect srcBounds;
QVector<QPointF> originalPoints;
QVector<QPointF> transformedPoints;
int pixelPrecision;
QSize gridSize;
bool result = false;
result =
KisDomUtils::findOnlyElement(e, "liquify_points", &liquifyEl) &&
KisDomUtils::loadValue(liquifyEl, "srcBounds", &srcBounds) &&
KisDomUtils::loadValue(liquifyEl, "originalPoints", &originalPoints) &&
KisDomUtils::loadValue(liquifyEl, "transformedPoints", &transformedPoints) &&
KisDomUtils::loadValue(liquifyEl, "pixelPrecision", &pixelPrecision) &&
KisDomUtils::loadValue(liquifyEl, "gridSize", &gridSize);
if (!result) {
- qWarning() << "WARNING: Failed to load liquify worker from XML";
+ warnKrita << "WARNING: Failed to load liquify worker from XML";
return new KisLiquifyTransformWorker(QRect(0,0,1024, 1024), 0, 8);
}
KisLiquifyTransformWorker *worker =
new KisLiquifyTransformWorker(srcBounds, 0, pixelPrecision);
const int numPoints = originalPoints.size();
if (numPoints != transformedPoints.size() ||
numPoints != worker->m_d->originalPoints.size() ||
gridSize != worker->m_d->gridSize) {
- qWarning() << "WARNING: Inconsistent number of points!";
- qWarning() << ppVar(originalPoints.size());
- qWarning() << ppVar(transformedPoints.size());
- qWarning() << ppVar(gridSize);
- qWarning() << ppVar(worker->m_d->originalPoints.size());
- qWarning() << ppVar(worker->m_d->transformedPoints.size());
- qWarning() << ppVar(worker->m_d->gridSize);
+ warnKrita << "WARNING: Inconsistent number of points!";
+ warnKrita << ppVar(originalPoints.size());
+ warnKrita << ppVar(transformedPoints.size());
+ warnKrita << ppVar(gridSize);
+ warnKrita << ppVar(worker->m_d->originalPoints.size());
+ warnKrita << ppVar(worker->m_d->transformedPoints.size());
+ warnKrita << ppVar(worker->m_d->gridSize);
return worker;
}
for (int i = 0; i < numPoints; i++) {
worker->m_d->originalPoints[i] = originalPoints[i];
worker->m_d->transformedPoints[i] = transformedPoints[i];
}
return worker;
}
diff --git a/krita/image/kis_node_filter_interface.cpp b/krita/image/kis_node_filter_interface.cpp
index fe44fb24c14..23100e18f85 100644
--- a/krita/image/kis_node_filter_interface.cpp
+++ b/krita/image/kis_node_filter_interface.cpp
@@ -1,92 +1,92 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_node_filter_interface.h"
#include "filter/kis_filter.h"
#include "generator/kis_generator.h"
#include "filter/kis_filter_registry.h"
#include "filter/kis_filter_configuration.h"
#include "generator/kis_generator_registry.h"
#ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER
#define SANITY_ACQUIRE_FILTER(filter) \
do { \
if ((filter)) { \
(filter)->sanityRefUsageCounter(); \
} \
} while (0)
#define SANITY_RELEASE_FILTER(filter) \
do { \
if (m_filter && m_filter->sanityDerefUsageCounter()) { \
- qWarning(); \
- qWarning() << "WARNING: filter configuration has more than one user! Krita will probably crash soon!"; \
- qWarning() << "WARNING:" << ppVar(this); \
- qWarning() << "WARNING:" << ppVar(filter.data()); \
- qWarning(); \
+ warnKrita; \
+ warnKrita << "WARNING: filter configuration has more than one user! Krita will probably crash soon!"; \
+ warnKrita << "WARNING:" << ppVar(this); \
+ warnKrita << "WARNING:" << ppVar(filter.data()); \
+ warnKrita; \
} \
} while (0)
#else /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */
#define SANITY_ACQUIRE_FILTER(filter)
#define SANITY_RELEASE_FILTER(filter)
#endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER*/
KisNodeFilterInterface::KisNodeFilterInterface(KisFilterConfiguration *filterConfig, bool useGeneratorRegistry)
: m_filter(filterConfig),
m_useGeneratorRegistry(useGeneratorRegistry)
{
SANITY_ACQUIRE_FILTER(m_filter);
}
KisNodeFilterInterface::KisNodeFilterInterface(const KisNodeFilterInterface &rhs)
: m_useGeneratorRegistry(rhs.m_useGeneratorRegistry)
{
if (m_useGeneratorRegistry) {
m_filter = KisSafeFilterConfigurationSP(KisGeneratorRegistry::instance()->cloneConfiguration(rhs.m_filter.data()));
} else {
m_filter = KisSafeFilterConfigurationSP(KisFilterRegistry::instance()->cloneConfiguration(rhs.m_filter.data()));
}
SANITY_ACQUIRE_FILTER(m_filter);
}
KisNodeFilterInterface::~KisNodeFilterInterface()
{
SANITY_RELEASE_FILTER(m_filter);
}
KisSafeFilterConfigurationSP KisNodeFilterInterface::filter() const
{
return m_filter;
}
void KisNodeFilterInterface::setFilter(KisFilterConfiguration *filterConfig)
{
SANITY_RELEASE_FILTER(m_filter);
Q_ASSERT(filterConfig);
m_filter = KisSafeFilterConfigurationSP(filterConfig);
SANITY_ACQUIRE_FILTER(m_filter);
}
diff --git a/krita/image/kis_paint_device.cc b/krita/image/kis_paint_device.cc
index 914ec049c1d..5a408736c60 100644
--- a/krita/image/kis_paint_device.cc
+++ b/krita/image/kis_paint_device.cc
@@ -1,1198 +1,1218 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paint_device.h"
#include <QRect>
#include <QTransform>
#include <QImage>
#include <QList>
#include <QHash>
#include <QIODevice>
#include <klocale.h>
#include <KoChannelInfo.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoIntegerMaths.h>
#include "kis_global.h"
#include "kis_types.h"
#include "kis_random_sub_accessor.h"
#include "kis_selection.h"
#include "kis_node.h"
#include "commands/kis_paintdevice_convert_type_command.h"
#include "kis_datamanager.h"
#include "kis_paint_device_writer.h"
#include "kis_selection_component.h"
#include "kis_pixel_selection.h"
#include "kis_repeat_iterators_pixel.h"
#include "kis_fixed_paint_device.h"
#include "tiles3/kis_hline_iterator.h"
#include "tiles3/kis_vline_iterator.h"
#include "tiles3/kis_random_accessor.h"
#include "kis_default_bounds.h"
#include "kis_lock_free_cache.h"
class PaintDeviceCache
{
public:
PaintDeviceCache(KisPaintDevice *paintDevice)
: m_paintDevice(paintDevice),
m_exactBoundsCache(paintDevice),
m_nonDefaultPixelAreaCache(paintDevice),
m_regionCache(paintDevice)
{
}
void setupCache() {
invalidate();
}
void invalidate() {
m_thumbnailsValid = false;
m_exactBoundsCache.invalidate();
m_nonDefaultPixelAreaCache.invalidate();
m_regionCache.invalidate();
}
QRect exactBounds() {
return m_exactBoundsCache.getValue();
}
QRect nonDefaultPixelArea() {
return m_nonDefaultPixelAreaCache.getValue();
}
QRegion region() {
return m_regionCache.getValue();
}
QImage createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
QImage thumbnail;
if(m_thumbnailsValid) {
thumbnail = findThumbnail(w, h);
}
else {
m_thumbnails.clear();
m_thumbnailsValid = true;
}
if(thumbnail.isNull()) {
thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), renderingIntent, conversionFlags);
cacheThumbnail(w, h, thumbnail);
}
Q_ASSERT(!thumbnail.isNull() || m_paintDevice->extent().isEmpty());
return thumbnail;
}
private:
inline QImage findThumbnail(qint32 w, qint32 h) {
QImage resultImage;
if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h)) {
resultImage = m_thumbnails[w][h];
}
return resultImage;
}
inline void cacheThumbnail(qint32 w, qint32 h, QImage image) {
m_thumbnails[w][h] = image;
}
private:
KisPaintDevice *m_paintDevice;
struct ExactBoundsCache : KisLockFreeCache<QRect> {
ExactBoundsCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
QRect calculateNewValue() const {
return m_paintDevice->calculateExactBounds(false);
}
private:
KisPaintDevice *m_paintDevice;
};
struct NonDefaultPixelCache : KisLockFreeCache<QRect> {
NonDefaultPixelCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
QRect calculateNewValue() const {
return m_paintDevice->calculateExactBounds(true);
}
private:
KisPaintDevice *m_paintDevice;
};
struct RegionCache : KisLockFreeCache<QRegion> {
RegionCache(KisPaintDevice *paintDevice) : m_paintDevice(paintDevice) {}
QRegion calculateNewValue() const {
return m_paintDevice->dataManager()->region();
}
private:
KisPaintDevice *m_paintDevice;
};
ExactBoundsCache m_exactBoundsCache;
NonDefaultPixelCache m_nonDefaultPixelAreaCache;
RegionCache m_regionCache;
bool m_thumbnailsValid;
QMap<int, QMap<int, QImage> > m_thumbnails;
};
struct Q_DECL_HIDDEN KisPaintDevice::Private
{
class KisPaintDeviceStrategy;
class KisPaintDeviceWrappedStrategy;
Private(KisPaintDevice *paintDevice);
KisPaintDevice *q;
KisDataManagerSP dataManager;
KisNodeWSP parent;
KisDefaultBoundsBaseSP defaultBounds;
PaintDeviceCache cache;
qint32 x;
qint32 y;
const KoColorSpace* colorSpace;
QScopedPointer<KisPaintDeviceStrategy> basicStrategy;
QScopedPointer<KisPaintDeviceWrappedStrategy> wrappedStrategy;
KisPaintDeviceStrategy* currentStrategy();
};
#include "kis_paint_device_strategies.h"
KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
: q(paintDevice),
cache(paintDevice),
x(0),
y(0),
basicStrategy(new KisPaintDeviceStrategy(paintDevice, this))
{
}
KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy()
{
if (!defaultBounds->wrapAroundMode()) {
return basicStrategy.data();
}
QRect wrapRect = defaultBounds->bounds();
if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) {
wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(defaultBounds->bounds(), q, this));
}
return wrappedStrategy.data();
}
KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(0, colorSpace, new KisDefaultBounds(), 0, name);
}
KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(0, colorSpace, defaultBounds, parent, name);
}
KisPaintDevice::KisPaintDevice(KisDataManagerSP explicitDataManager,
KisPaintDeviceSP src,
const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(explicitDataManager, src->colorSpace(), src->defaultBounds(), 0, name);
m_d->x = src->x();
m_d->y = src->y();
}
void KisPaintDevice::init(KisDataManagerSP explicitDataManager,
const KoColorSpace *colorSpace,
KisDefaultBoundsBaseSP defaultBounds,
KisNodeWSP parent, const QString& name)
{
Q_ASSERT(colorSpace);
setObjectName(name);
if (!defaultBounds) {
defaultBounds = new KisDefaultBounds();
}
m_d->colorSpace = colorSpace;
Q_ASSERT(m_d->colorSpace);
if(explicitDataManager) {
m_d->dataManager = explicitDataManager;
}
else {
const qint32 pixelSize = colorSpace->pixelSize();
quint8* defaultPixel = new quint8[colorSpace->pixelSize()];
colorSpace->fromQColor(Qt::transparent, defaultPixel);
m_d->dataManager = new KisDataManager(pixelSize, defaultPixel);
delete[] defaultPixel;
Q_CHECK_PTR(m_d->dataManager);
}
m_d->cache.setupCache();
setDefaultBounds(defaultBounds);
setParentNode(parent);
}
KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs)
: QObject()
, KisShared()
, m_d(new Private(this))
{
if (this != &rhs) {
m_d->colorSpace = rhs.m_d->colorSpace;
Q_ASSERT(m_d->colorSpace);
m_d->x = rhs.m_d->x;
m_d->y = rhs.m_d->y;
Q_ASSERT(rhs.m_d->dataManager);
m_d->dataManager = new KisDataManager(*rhs.m_d->dataManager);
Q_CHECK_PTR(m_d->dataManager);
m_d->cache.setupCache();
setDefaultBounds(rhs.defaultBounds());
setParentNode(0);
}
}
KisPaintDevice::~KisPaintDevice()
{
delete m_d;
}
void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
{
clear();
m_d->x = src->x();
m_d->y = src->y();
if(!(*colorSpace() == *src->colorSpace())) {
if (m_d->colorSpace->pixelSize() != src->colorSpace()->pixelSize()) {
m_d->dataManager = 0;
m_d->dataManager = new KisDataManager(src->pixelSize(), src->defaultPixel());
m_d->cache.setupCache();
}
m_d->colorSpace = src->colorSpace();
}
setDefaultPixel(src->defaultPixel());
setDefaultBounds(src->defaultBounds());
Q_ASSERT(fastBitBltPossible(src));
}
void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
{
prepareClone(src);
// we guarantee that *this is totally empty, so copy pixels that
// are areally present on the source image only
const QRect optimizedRect = rect & src->extent();
fastBitBlt(src, optimizedRect);
}
void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
{
prepareClone(src);
// we guarantee that *this is totally empty, so copy pixels that
// are areally present on the source image only
const QRect optimizedRect = minimalRect & src->extent();
fastBitBltRough(src, optimizedRect);
}
void KisPaintDevice::setDirty()
{
m_d->cache.invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty();
}
void KisPaintDevice::setDirty(const QRect & rc)
{
m_d->cache.invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(rc);
}
void KisPaintDevice::setDirty(const QRegion & region)
{
m_d->cache.invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(region);
}
void KisPaintDevice::setDirty(const QVector<QRect> rects)
{
m_d->cache.invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(rects);
}
void KisPaintDevice::setParentNode(KisNodeWSP parent)
{
m_d->parent = parent;
}
// for testing purposes only
KisNodeWSP KisPaintDevice::parentNode() const
{
return m_d->parent;
}
void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds)
{
m_d->defaultBounds = defaultBounds;
m_d->cache.invalidate();
}
KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const
{
return m_d->defaultBounds;
}
void KisPaintDevice::move(const QPoint &pt)
{
m_d->currentStrategy()->move(pt);
}
void KisPaintDevice::move(qint32 x, qint32 y)
{
move(QPoint(x, y));
}
void KisPaintDevice::setX(qint32 x)
{
move(QPoint(x, m_d->y));
}
void KisPaintDevice::setY(qint32 y)
{
move(QPoint(m_d->x, y));
}
qint32 KisPaintDevice::x() const
{
return m_d->x;
}
qint32 KisPaintDevice::y() const
{
return m_d->y;
}
void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
{
QRect rc = extent();
x = rc.x();
y = rc.y();
w = rc.width();
h = rc.height();
}
QRect KisPaintDevice::extent() const
{
return m_d->currentStrategy()->extent();
}
QRegion KisPaintDevice::region() const
{
return m_d->currentStrategy()->region();
}
QRect KisPaintDevice::nonDefaultPixelArea() const
{
return m_d->cache.nonDefaultPixelArea();
}
QRect KisPaintDevice::exactBounds() const
{
return m_d->cache.exactBounds();
}
namespace Impl {
struct CheckFullyTransparent
{
CheckFullyTransparent(const KoColorSpace *colorSpace)
: m_colorSpace(colorSpace)
{
}
bool isPixelEmpty(const quint8 *pixelData) {
return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8;
}
private:
const KoColorSpace *m_colorSpace;
};
struct CheckNonDefault
{
CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
: m_pixelSize(pixelSize),
m_defaultPixel(defaultPixel)
{
}
bool isPixelEmpty(const quint8 *pixelData) {
return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0;
}
private:
int m_pixelSize;
const quint8 *m_defaultPixel;
};
template <class ComparePixelOp>
QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp)
{
if (startRect == endRect) return startRect;
// Solution n°2
int x, y, w, h;
int boundLeft, boundTop, boundRight, boundBottom;
int endDirN, endDirE, endDirS, endDirW;
startRect.getRect(&x, &y, &w, &h);
if (endRect.isEmpty()) {
endDirS = startRect.bottom();
endDirN = startRect.top();
endDirE = startRect.right();
endDirW = startRect.left();
startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
} else {
endDirS = endRect.top() - 1;
endDirN = endRect.bottom() + 1;
endDirE = endRect.left() - 1;
endDirW = endRect.right() + 1;
endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
}
// XXX: a small optimization is possible by using H/V line iterators in the first
// and third cases, at the cost of making the code a bit more complex
KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG(x, y);
bool found = false;
{
for (qint32 y2 = y; y2 <= endDirS; ++y2) {
for (qint32 x2 = x; x2 < x + w || found; ++ x2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundTop = y2;
found = true;
break;
}
}
if (found) break;
}
}
/**
* If the first pass hasn't found any opaque pixel, there is no
* reason to check that 3 more times. They will not appear in the
* meantime. Just return an empty bounding rect.
*/
if (!found && endRect.isEmpty()) {
return QRect();
}
found = false;
for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) {
for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundBottom = y2;
found = true;
break;
}
}
if (found) break;
}
found = false;
{
for (qint32 x2 = x; x2 <= endDirE ; ++x2) {
for (qint32 y2 = y; y2 < y + h || found; ++y2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundLeft = x2;
found = true;
break;
}
}
if (found) break;
}
}
found = false;
// Look for right edge )
{
for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) {
for (qint32 y2 = y + h -1; y2 >= y || found; --y2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundRight = x2;
found = true;
break;
}
}
if (found) break;
}
}
return QRect(boundLeft, boundTop,
boundRight - boundLeft + 1,
boundBottom - boundTop + 1);
}
}
QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const
{
QRect startRect = extent();
QRect endRect;
quint8 defaultOpacity = m_d->colorSpace->opacityU8(defaultPixel());
if(defaultOpacity != OPACITY_TRANSPARENT_U8) {
if (!nonDefaultOnly) {
/**
* We will calculate exact bounds only outside of the
* image bounds, and that'll be nondefault area only.
*/
endRect = defaultBounds()->bounds();
nonDefaultOnly = true;
} else {
startRect = region().boundingRect();
}
}
if (nonDefaultOnly) {
Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel());
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
} else {
Impl::CheckFullyTransparent compareOp(m_d->colorSpace);
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
}
return endRect;
}
void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h)
{
crop(QRect(x, y, w, h));
}
void KisPaintDevice::crop(const QRect &rect)
{
m_d->currentStrategy()->crop(rect);
}
void KisPaintDevice::purgeDefaultPixels()
{
m_d->dataManager->purge(m_d->dataManager->extent());
}
void KisPaintDevice::setDefaultPixel(const quint8 *defPixel)
{
m_d->dataManager->setDefaultPixel(defPixel);
m_d->cache.invalidate();
}
const quint8 *KisPaintDevice::defaultPixel() const
{
return m_d->dataManager->defaultPixel();
}
void KisPaintDevice::clear()
{
m_d->dataManager->clear();
m_d->cache.invalidate();
}
void KisPaintDevice::clear(const QRect & rc)
{
m_d->currentStrategy()->clear(rc);
}
void KisPaintDevice::fill(const QRect & rc, const KoColor &color)
{
Q_ASSERT(*color.colorSpace() == *colorSpace());
m_d->currentStrategy()->fill(rc, color.data());
}
void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel)
{
m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel);
}
bool KisPaintDevice::write(KisPaintDeviceWriter &store)
{
return m_d->dataManager->write(store);
}
bool KisPaintDevice::read(QIODevice *stream)
{
bool retval = m_d->dataManager->read(stream);
m_d->cache.invalidate();
return retval;
}
KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
m_d->cache.invalidate();
dbgImage << this << colorSpace()->id() << dstColorSpace->id() << renderingIntent << conversionFlags;
if (*colorSpace() == *dstColorSpace) {
return 0;
}
KisPaintDevice dst(dstColorSpace);
dst.setX(x());
dst.setY(y());
qint32 x, y, w, h;
QRect rc = exactBounds();
x = rc.x();
y = rc.y();
w = rc.width();
h = rc.height();
if (w == 0 || h == 0) {
quint8 *defPixel = new quint8[dstColorSpace->pixelSize()];
memset(defPixel, 0, pixelSize());
m_d->colorSpace->convertPixelsTo(defaultPixel(), defPixel, dstColorSpace, 1, renderingIntent, conversionFlags);
setDefaultPixel(defPixel);
delete[] defPixel;
}
else {
KisRandomConstAccessorSP srcIt = createRandomConstAccessorNG(x, y);
KisRandomAccessorSP dstIt = dst.createRandomAccessorNG(x, y);
for (qint32 row = y; row < y + h; ++row) {
qint32 column = x;
qint32 columnsRemaining = w;
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(column);
qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(column);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, columnsRemaining);
srcIt->moveTo(column, row);
dstIt->moveTo(column, row);
const quint8 *srcData = srcIt->rawDataConst();
quint8 *dstData = dstIt->rawData();
m_d->colorSpace->convertPixelsTo(srcData, dstData, dstColorSpace, columns, renderingIntent, conversionFlags);
column += columns;
columnsRemaining -= columns;
}
}
}
KisDataManagerSP oldData = m_d->dataManager;
const KoColorSpace *oldColorSpace = m_d->colorSpace;
KisPaintDeviceConvertTypeCommand* cmd = new KisPaintDeviceConvertTypeCommand(this,
oldData,
oldColorSpace,
dst.m_d->dataManager,
dstColorSpace);
setDataManager(dst.m_d->dataManager, dstColorSpace);
return cmd;
}
void KisPaintDevice::setProfile(const KoColorProfile * profile)
{
if (profile == 0) return;
m_d->cache.invalidate();
const KoColorSpace * dstSpace =
KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
if (dstSpace) {
m_d->colorSpace = dstSpace;
}
emit profileChanged(profile);
}
void KisPaintDevice::setDataManager(KisDataManagerSP data, const KoColorSpace * colorSpace)
{
m_d->dataManager = data;
m_d->cache.setupCache();
if(colorSpace) {
m_d->colorSpace = colorSpace;
emit colorSpaceChanged(colorSpace);
}
}
void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile,
qint32 offsetX, qint32 offsetY)
{
QImage image = _image;
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// Don't convert if not no profile is given and both paint dev and qimage are rgba.
if (!profile && colorSpace()->id() == "RGBA") {
writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height());
} else {
try {
quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()];
KoColorSpaceRegistry::instance()
->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile)
->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(),
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
writeBytes(dstData, offsetX, offsetY, image.width(), image.height());
delete[] dstData;
} catch (std::bad_alloc) {
warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes";
return;
}
}
m_d->cache.invalidate();
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
qint32 x1;
qint32 y1;
qint32 w;
qint32 h;
QRect rc = exactBounds();
x1 = rc.x();
y1 = rc.y();
w = rc.width();
h = rc.height();
return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
return convertToQImage(dstProfile,
rc.x(), rc.y(), rc.width(), rc.height(),
renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile * dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
if (w < 0)
return QImage();
if (h < 0)
return QImage();
quint8 *data = 0;
try {
data = new quint8 [w * h * pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize();
//delete[] data; // data is not allocated, so don't free it
return QImage();
}
Q_CHECK_PTR(data);
// XXX: Is this really faster than converting line by line and building the QImage directly?
// This copies potentially a lot of data.
readBytes(data, x1, y1, w, h);
QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags);
delete[] data;
return image;
}
KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect) const
{
KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace());
int srcWidth, srcHeight;
int srcX0, srcY0;
QRect e = rect.isValid() ? rect : extent();
e.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight);
if (w > srcWidth) {
w = srcWidth;
h = qint32(double(srcWidth) / w * h);
}
if (h > srcHeight) {
h = srcHeight;
w = qint32(double(srcHeight) / h * w);
}
if (srcWidth > srcHeight)
h = qint32(double(srcHeight) / srcWidth * w);
else if (srcHeight > srcWidth)
w = qint32(double(srcWidth) / srcHeight * h);
const qint32 pixelSize = this->pixelSize();
KisRandomConstAccessorSP iter = createRandomConstAccessorNG(0, 0);
KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0);
for (qint32 y = 0; y < h; ++y) {
qint32 iY = srcY0 + (y * srcHeight) / h;
for (qint32 x = 0; x < w; ++x) {
qint32 iX = srcX0 + (x * srcWidth) / w;
iter->moveTo(iX, iY);
dstIter->moveTo(x, y);
memcpy(dstIter->rawData(), iter->rawDataConst(), pixelSize);
}
}
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
KisPaintDeviceSP dev = createThumbnailDevice(w, h, rect);
QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags);
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
return m_d->cache.createThumbnail(w, h, renderingIntent, conversionFlags);
}
KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
{
m_d->cache.invalidate();
return m_d->currentStrategy()->createHLineIteratorNG(x, y, w);
}
KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
{
return m_d->currentStrategy()->createHLineConstIteratorNG(x, y, w);
}
KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w)
{
m_d->cache.invalidate();
return m_d->currentStrategy()->createVLineIteratorNG(x, y, w);
}
KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
{
return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w);
}
KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const
{
KisDataManager* dm = const_cast< KisDataManager*>(m_d->dataManager.data());
return new KisRepeatHLineConstIteratorNG(dm, x, y, w, m_d->x, m_d->y, _dataWidth);
}
KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const
{
KisDataManager* dm = const_cast< KisDataManager*>(m_d->dataManager.data());
return new KisRepeatVLineConstIteratorNG(dm, x, y, h, m_d->x, m_d->y, _dataWidth);
}
KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG(qint32 x, qint32 y)
{
m_d->cache.invalidate();
return m_d->currentStrategy()->createRandomAccessorNG(x, y);
}
KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG(qint32 x, qint32 y) const
{
return m_d->currentStrategy()->createRandomConstAccessorNG(x, y);
}
KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const
{
KisPaintDevice* pd = const_cast<KisPaintDevice*>(this);
return new KisRandomSubAccessor(pd);
}
void KisPaintDevice::clearSelection(KisSelectionSP selection)
{
QRect r = selection->selectedExactRect() & m_d->defaultBounds->bounds();
if (r.isValid()) {
KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width());
KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width());
const quint8* defaultPixel_ = defaultPixel();
bool transparentDefault = (m_d->colorSpace->opacityU8(defaultPixel_) == OPACITY_TRANSPARENT_U8);
for (qint32 y = 0; y < r.height(); y++) {
do {
// XXX: Optimize by using stretches
m_d->colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1);
if (transparentDefault && m_d->colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) {
memcpy(devIt->rawData(), defaultPixel_, m_d->colorSpace->pixelSize());
}
} while (devIt->nextPixel() && selectionIt->nextPixel());
devIt->nextRow();
selectionIt->nextRow();
}
m_d->dataManager->purge(r.translated(-m_d->x, -m_d->y));
setDirty(r);
}
}
bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const
{
KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->rawDataConst();
if (!pix) return false;
colorSpace()->toQColor(pix, c);
return true;
}
bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const
{
KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->rawDataConst();
if (!pix) return false;
kc->setColor(pix, m_d->colorSpace);
return true;
}
bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c)
{
KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
colorSpace()->fromQColor(c, iter->rawData());
m_d->cache.invalidate();
return true;
}
bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc)
{
const quint8 * pix;
KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
if (kc.colorSpace() != m_d->colorSpace) {
KoColor kc2(kc, m_d->colorSpace);
pix = kc2.data();
memcpy(iter->rawData(), pix, m_d->colorSpace->pixelSize());
} else {
pix = kc.data();
memcpy(iter->rawData(), pix, m_d->colorSpace->pixelSize());
}
m_d->cache.invalidate();
return true;
}
bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src)
{
return m_d->x == src->x() && m_d->y == src->y() &&
*colorSpace() == *src->colorSpace();
}
void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBlt(src, rect);
}
void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltOldData(src, rect);
}
void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltRough(src, rect);
}
void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltRoughOldData(src, rect);
}
void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const
{
readBytes(data, QRect(x, y, w, h));
}
void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const
{
m_d->currentStrategy()->readBytes(data, rect);
}
void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
{
writeBytes(data, QRect(x, y, w, h));
}
void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect)
{
m_d->currentStrategy()->writeBytes(data, rect);
}
QVector<quint8*> KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const
{
return m_d->currentStrategy()->readPlanarBytes(x, y, w, h);
}
void KisPaintDevice::writePlanarBytes(QVector<quint8*> planes, qint32 x, qint32 y, qint32 w, qint32 h)
{
m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h);
}
KisDataManagerSP KisPaintDevice::dataManager() const
{
return m_d->dataManager;
}
quint32 KisPaintDevice::pixelSize() const
{
quint32 _pixelSize = m_d->colorSpace->pixelSize();
Q_ASSERT(_pixelSize > 0);
return _pixelSize;
}
quint32 KisPaintDevice::channelCount() const
{
quint32 _channelCount = m_d->colorSpace->channelCount();
Q_ASSERT(_channelCount > 0);
return _channelCount;
}
const KoColorSpace* KisPaintDevice::colorSpace() const
{
Q_ASSERT(m_d->colorSpace != 0);
return m_d->colorSpace;
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const
{
KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace());
device->setDefaultBounds(defaultBounds());
return device;
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const
{
KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource);
clone->setDefaultBounds(defaultBounds());
clone->convertTo(compositionSourceColorSpace(),
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
return clone;
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const
{
KisPaintDeviceSP clone = new KisPaintDevice(colorSpace());
clone->setDefaultBounds(defaultBounds());
clone->makeCloneFromRough(cloneSource, roughRect);
clone->convertTo(compositionSourceColorSpace(),
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
return clone;
}
KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const
{
return new KisFixedPaintDevice(compositionSourceColorSpace());
}
const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const
{
return colorSpace();
}
QVector<qint32> KisPaintDevice::channelSizes() const
{
QVector<qint32> sizes;
QList<KoChannelInfo*> channels = colorSpace()->channels();
qSort(channels);
foreach(KoChannelInfo * channelInfo, channels) {
sizes.append(channelInfo->size());
}
return sizes;
}
KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject()
{
KisDataManager::releaseInternalPools();
}
KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject()
{
return new MemoryReleaseObject();
}
+void kis_debug_save_device_incremental(KisPaintDeviceSP device,
+ int i,
+ const QRect &rc,
+ const QString &suffix, const QString &prefix)
+{
+ QString filename = QString("%1_%2.png").arg(i).arg(suffix);
+
+ if (!prefix.isEmpty()) {
+ filename = QString("%1_%2.png").arg(prefix).arg(filename);
+ }
+
+ QRect saveRect(rc);
+
+ if (saveRect.isEmpty()) {
+ saveRect = device->exactBounds();
+ }
+
+ dbgKrita << "Dumping:" << filename;
+ device->convertToQImage(0, saveRect).save(filename);
+}
diff --git a/krita/image/kis_paint_device.h b/krita/image/kis_paint_device.h
index 571026a0740..00a6d1aba35 100644
--- a/krita/image/kis_paint_device.h
+++ b/krita/image/kis_paint_device.h
@@ -1,786 +1,831 @@
/*
* Copyright (c) 2002 patrick julien <freak@codepimps.org>
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_PAINT_DEVICE_IMPL_H_
#define KIS_PAINT_DEVICE_IMPL_H_
#include <QObject>
#include <QRect>
#include <QVector>
#include "kis_debug.h"
#include <KoColorConversionTransformation.h>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_shared.h"
#include "kis_default_bounds_base.h"
#include <kritaimage_export.h>
class KUndo2Command;
class QRect;
class QImage;
class QPoint;
class QString;
class QColor;
class QIODevice;
class KoColor;
class KoColorSpace;
class KoColorProfile;
class KisDataManager;
class KisPaintDeviceWriter;
typedef KisSharedPtr<KisDataManager> KisDataManagerSP;
/**
* A paint device contains the actual pixel data and offers methods
* to read and write pixels. A paint device has an integer x,y position
* (i.e., are not positioned on the image with sub-pixel accuracy).
* A KisPaintDevice doesn't have any fixed size, the size changes dynamically
* when pixels are accessed by an iterator.
*/
class KRITAIMAGE_EXPORT KisPaintDevice
: public QObject
, public KisShared
{
Q_OBJECT
public:
/**
* Create a new paint device with the specified colorspace.
*
* @param colorSpace the colorspace of this paint device
* @param name for debugging purposes
*/
KisPaintDevice(const KoColorSpace * colorSpace, const QString& name = QString());
/**
* Create a new paint device with the specified colorspace. The
* parent node will be notified of changes to this paint device.
*
* @param parent the node that contains this paint device
* @param colorSpace the colorspace of this paint device
* @param defaultBounds boundaries of the device in case it is empty
* @param name for debugging purposes
*/
KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds = 0, const QString& name = QString());
KisPaintDevice(const KisPaintDevice& rhs);
virtual ~KisPaintDevice();
protected:
/**
* A special constructor for usage in KisPixelSelection. It allows
* two paint devices to share a data manager.
*
* @param explicitDataManager data manager to use inside paint device
* @param src source paint device to copy parameters from
* @param name for debugging purposes
*/
KisPaintDevice(KisDataManagerSP explicitDataManager,
KisPaintDeviceSP src, const QString& name = QString());
public:
/**
* Write the pixels of this paint device into the specified file store.
*/
bool write(KisPaintDeviceWriter &store);
/**
* Fill this paint device with the pixels from the specified file store.
*/
bool read(QIODevice *stream);
public:
/**
* set the parent node of the paint device
*/
void setParentNode(KisNodeWSP parent);
/**
* set the default bounds for the paint device when
* the default pixel in not completely transarent
*/
void setDefaultBounds(KisDefaultBoundsBaseSP bounds);
/**
* the default bounds rect of the paint device
*/
KisDefaultBoundsBaseSP defaultBounds() const;
/**
* Moves the device to these new coordinates (so no incremental move or so)
*/
void move(qint32 x, qint32 y);
/**
* Convenience method for the above.
*/
virtual void move(const QPoint& pt);
/**
* The X offset of the paint device
*/
qint32 x() const;
/**
* The Y offset of the paint device
*/
qint32 y() const;
/**
* set the X offset of the paint device
*/
void setX(qint32 x);
/**
* set the Y offset of the paint device
*/
void setY(qint32 y);
/**
* Retrieve the bounds of the paint device. The size is not exact,
* but may be larger if the underlying datamanager works that way.
* For instance, the tiled datamanager keeps the extent to the nearest
* multiple of 64.
*
* If default pixel is not transparent, then the actual extent
* rect is united with the defaultBounds()->bounds() value
* (the size of the image, usually).
*/
QRect extent() const;
/// Convience method for the above
void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const;
/**
* Get the exact bounds of this paint device. The real solution is
* very slow because it does a linear scanline search, but it
* uses caching, so calling to this function without changing
* the device is quite cheap.
*
* Exactbounds follows these rules:
*
* <ul>
* <li>if default pixel is transparent, then exact bounds
* of actual pixel data are returned
* <li>if default pixel is not transparent, then the union
* (defaultBounds()->bounds() | nonDefaultPixelArea()) is
* returned
* </ul>
* \see calculateExactBounds()
*/
QRect exactBounds() const;
/**
* Retuns exact rectangle of the paint device that contains
* non-default pixels. For paint devices with fully transparent
* default pixel is equivalent to exactBounds().
*
* nonDefaultPixelArea() follows these rules:
*
* <ul>
* <li>if default pixel is transparent, then exact bounds
* of actual pixel data are returned. The same as exactBounds()
* <li>if default pixel is not transparent, then calculates the
* rectangle of non-default pixels. May be smaller or greater
* than image bounds
* </ul>
* \see calculateExactBounds()
*/
QRect nonDefaultPixelArea() const;
/**
* Returns a rough approximation of region covered by device.
* For tiled data manager, it region will consist of a number
* of rects each corresponding to a tile.
*/
QRegion region() const;
/**
* Cut the paint device down to the specified rect. If the crop
* area is bigger than the paint device, nothing will happen.
*/
void crop(qint32 x, qint32 y, qint32 w, qint32 h);
/// Convience method for the above
void crop(const QRect & r);
/**
* Complete erase the current paint device. Its size will become 0. This
* does not take the selection into account.
*/
virtual void clear();
/**
* Clear the given rectangle to transparent black. The paint device will expand to
* contain the given rect.
*/
void clear(const QRect & rc);
/**
* Frees the memory occupied by the pixels containing default
* values. The extents() and exactBounds() of the image will
* probably also shrink
*/
void purgeDefaultPixels();
/**
* Sets the default pixel. New data will be initialised with this pixel. The pixel is copied: the
* caller still owns the pointer and needs to delete it to avoid memory leaks.
*/
void setDefaultPixel(const quint8 *defPixel);
/**
* Get a pointer to the default pixel.
*/
const quint8 *defaultPixel() const;
/**
* Fill the given rectangle with the given pixel. The paint device will expand to
* contain the given rect.
*/
void fill(const QRect & rc, const KoColor &color);
/**
* Overloaded function. For legacy purposes only.
* Please use fill(const QRect & rc, const KoColor &color) instead
*/
void fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel);
public:
/**
* Prepares the device for fastBitBlt opreration. It clears
* the device, switches x,y shifts and colorspace if needed.
* After this call fastBitBltPossible will return true.
* May be used for initialization of temporary devices.
*/
void prepareClone(KisPaintDeviceSP src);
/**
* Make this device to become a clone of \a src. It will have the same
* x,y shifts, colorspace and will share pixels inside \a rect.
* After calling this function:
* (this->extent() >= this->exactBounds() == rect).
*
* Rule of thumb:
*
* "Use makeCloneFrom() or makeCloneFromRough() if and only if you
* are the only owner of the destination paint device and you are
* 100% sure no other thread has access to it"
*/
void makeCloneFrom(KisPaintDeviceSP src, const QRect &rect);
/**
* Make this device to become a clone of \a src. It will have the same
* x,y shifts, colorspace and will share pixels inside \a rect.
* Be careful, this function will copy *at least* \a rect
* of pixels. Actual copy area will be a bigger - it will
* be aligned by tiles borders. So after calling this function:
* (this->extent() == this->exactBounds() >= rect).
*
* Rule of thumb:
*
* "Use makeCloneFrom() or makeCloneFromRough() if and only if you
* are the only owner of the destination paint device and you are
* 100% sure no other thread has access to it"
*/
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect);
protected:
friend class KisPaintDeviceTest;
friend class DataReaderThread;
/**
* Checks whether a src paint device can be used as source
* of fast bitBlt operation. The result of the check may
* depend on whether color spaces coinside, whether there is
* any shift of tiles between the devices and etc.
*
* WARNING: This check must be done <i>before</i> performing any
* fast bitBlt operation!
*
* \see fastBitBlt
* \see fastBitBltRough
*/
bool fastBitBltPossible(KisPaintDeviceSP src);
/**
* Clones rect from another paint device. The cloned area will be
* shared between both paint devices as much as possible using
* copy-on-write. Parts of the rect that cannot be shared
* (cross tiles) are deep-copied,
*
* \see fastBitBltPossible
* \see fastBitBltRough
*/
void fastBitBlt(KisPaintDeviceSP src, const QRect &rect);
/**
* The same as \ref fastBitBlt() but reads old data
*/
void fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect);
/**
* Clones rect from another paint device in a rough and fast way.
* All the tiles touched by rect will be shared, between both
* devices, that means it will copy a bigger area than was
* requested. This method is supposed to be used for bitBlt'ing
* into temporary paint devices.
*
* \see fastBitBltPossible
* \see fastBitBlt
*/
void fastBitBltRough(KisPaintDeviceSP src, const QRect &rect);
/**
* The same as \ref fastBitBltRough() but reads old data
*/
void fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect);
public:
/**
* Read the bytes representing the rectangle described by x, y, w, h into
* data. If data is not big enough, Krita will gladly overwrite the rest
* of your precious memory.
*
* Since this is a copy, you need to make sure you have enough memory.
*
* Reading from areas not previously initialized will read the default
* pixel value into data but not initialize that region.
*/
void readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Read the bytes representing the rectangle rect into
* data. If data is not big enough, Krita will gladly overwrite the rest
* of your precious memory.
*
* Since this is a copy, you need to make sure you have enough memory.
*
* Reading from areas not previously initialized will read the default
* pixel value into data but not initialize that region.
* @param data The address of the memory to receive the bytes read
* @param rect The rectangle in the paint device to read from
*/
void readBytes(quint8 * data, const QRect &rect) const;
/**
* Copy the bytes in data into the rect specified by x, y, w, h. If the
* data is too small or uninitialized, Krita will happily read parts of
* memory you never wanted to be read.
*
* If the data is written to areas of the paint device not previously initialized,
* the paint device will grow.
*/
void writeBytes(const quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h);
/**
* Copy the bytes in data into the rectangle rect. If the
* data is too small or uninitialized, Krita will happily read parts of
* memory you never wanted to be read.
*
* If the data is written to areas of the paint device not previously initialized,
* the paint device will grow.
* @param data The address of the memory to write bytes from
* @param rect The rectangle in the paint device to write to
*/
void writeBytes(const quint8 * data, const QRect &rect);
/**
* Copy the bytes in the paint device into a vector of arrays of bytes,
* where the number of arrays is the number of channels in the
* paint device. If the specified area is larger than the paint
* device's extent, the default pixel will be read.
*/
QVector<quint8*> readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Write the data in the separate arrays to the channes. If there
* are less vectors than channels, the remaining channels will not
* be copied. If any of the arrays points to 0, the channel in
* that location will not be touched. If the specified area is
* larger than the paint device, the paint device will be
* extended. There are no guards: if the area covers more pixels
* than there are bytes in the arrays, krita will happily fill
* your paint device with areas of memory you never wanted to be
* read. Krita may also crash.
*
* XXX: what about undo?
*/
void writePlanarBytes(QVector<quint8*> planes, qint32 x, qint32 y, qint32 w, qint32 h);
/**
* Converts the paint device to a different colorspace
*
* @return a command that can be used to undo the conversion.
*/
KUndo2Command* convertTo(const KoColorSpace * dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags);
/**
* Changes the profile of the colorspace of this paint device to the given
* profile. If the given profile is 0, nothing happens.
*/
void setProfile(const KoColorProfile * profile);
/**
* Fill this paint device with the data from image; starting at (offsetX, offsetY)
* @param srcProfileName name of the RGB profile to interpret the image as. 0 is interpreted as sRGB
*/
void convertFromQImage(const QImage& image, const KoColorProfile *profile, qint32 offsetX = 0, qint32 offsetY = 0);
/**
* Create an RGBA QImage from a rectangle in the paint device.
*
* @param x Left coordinate of the rectangle
* @param y Top coordinate of the rectangle
* @param w Width of the rectangle in pixels
* @param h Height of the rectangle in pixels
* @param dstProfile RGB profile to use in conversion. May be 0, in which
* case it's up to the color strategy to choose a profile (most
* like sRGB).
*/
virtual QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags) const;
/**
* Overridden method for convenience
*/
QImage convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags) const;
/**
* Create an RGBA QImage from a rectangle in the paint device. The
* rectangle is defined by the parent image's bounds.
*
* @param dstProfile RGB profile to use in conversion. May be 0, in which
* case it's up to the color strategy to choose a profile (most
* like sRGB).
*/
QImage convertToQImage(const KoColorProfile * dstProfile,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags) const;
/**
* Creates a paint device thumbnail of the paint device, retaining
* the aspect ratio. The width and height of the returned device
* won't exceed \p maxw and \p maxw, but they may be smaller.
*
* @param maxw: maximum width
* @param maxh: maximum height
* @param rect: only this rect will be used for the thumbnail
*
*/
KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect()) const;
/**
* Creates a thumbnail of the paint device, retaining the aspect ratio.
* The width and height of the returned QImage won't exceed \p maxw and \p maxw, but they may be smaller.
* The colors are not corrected for display!
*
* @param maxw: maximum width
* @param maxh: maximum height
* @param rect: only this rect will be used for the thumbnail
*/
QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags);
/**
* Cached version of createThumbnail(qint32 maxw, qint32 maxh, const KisSelection *selection, QRect rect)
*/
QImage createThumbnail(qint32 maxw, qint32 maxh,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags);
/**
* Fill c and opacity with the values found at x and y.
*
* The color values will be transformed from the profile of
* this paint device to the display profile.
*
* @return true if the operation was successful.
*/
bool pixel(qint32 x, qint32 y, QColor *c) const;
/**
* Fill kc with the values found at x and y. This method differs
* from the above in using KoColor, which can be of any colorspace
*
* The color values will be transformed from the profile of
* this paint device to the display profile.
*
* @return true if the operation was successful.
*/
bool pixel(qint32 x, qint32 y, KoColor * kc) const;
/**
* Set the specified pixel to the specified color. Note that this
* bypasses KisPainter. the PaintDevice is here used as an equivalent
* to QImage, not QPixmap. This means that this is not undoable; also,
* there is no compositing with an existing value at this location.
*
* The color values will be transformed from the display profile to
* the paint device profile.
*
* Note that this will use 8-bit values and may cause a significant
* degradation when used on 16-bit or hdr quality images.
*
* @return true if the operation was successful
*/
bool setPixel(qint32 x, qint32 y, const QColor& c);
/// Convience method for the above
bool setPixel(qint32 x, qint32 y, const KoColor& kc);
/**
* @return the colorspace of the pixels in this paint device
*/
const KoColorSpace* colorSpace() const;
/**
* There is quite a common technique in Krita. It is used in
* cases, when we want to paint something over a paint device
* using the composition, opacity or selection. E.g. painting a
* dab in a paint op, filling the selection in the Fill Tool.
* Such work is usually done in the following way:
*
* 1) Create a paint device
*
* 2) Fill it with the desired color or data
*
* 3) Create a KisPainter and set all the properties of the
* trasaction: selection, compositeOp, opacity and etc.
*
* 4) Paint a newly created paint device over the destination
* device.
*
* The following two methods (createCompositionSourceDevice() or
* createCompositionSourceDeviceFixed())should be used for the
* accomplishing the step 1). The point is that the desired color
* space of the temporary device may not coincide with the color
* space of the destination. That is the case, for example, for
* the alpha8() colorspace used in the selections. So for such
* devices the temporary target would have a different (grayscale)
* color space.
*
* So there are two rules of thumb:
*
* 1) If you need a temporary device which you are going to fill
* with some data and then paint over the paint device, create
* it with either createCompositionSourceDevice() or
* createCompositionSourceDeviceFixed().
*
* 2) Do *not* expect that the color spaces of the destination and
* the temporary device would coincide. If you need to copy a
* single pixel from one device to another, you can use
* KisCrossDeviceColorPicker class, that will handle all the
* necessary conversions for you.
*
* \see createCompositionSourceDeviceFixed()
* \see compositionSourceColorSpace()
* \see KisCrossDeviceColorPicker
* \see KisCrossDeviceColorPickerInt
*/
KisPaintDeviceSP createCompositionSourceDevice() const;
/**
* The same as createCompositionSourceDevice(), but initializes the
* newly created device with the content of \p cloneSource
*
* \see createCompositionSourceDevice()
*/
KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const;
/**
* The same as createCompositionSourceDevice(), but initializes
* the newly created device with the *rough* \p roughRect of
* \p cloneSource.
*
* "Rough rect" means that it may copy a bit more than
* requested. It is expected that the caller will not use the area
* outside \p roughRect.
*
* \see createCompositionSourceDevice()
*/
KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const;
/**
* This is a convenience method for createCompositionSourceDevice()
*
* \see createCompositionSourceDevice()
*/
KisFixedPaintDeviceSP createCompositionSourceDeviceFixed() const;
/**
* This is a lowlevel method for the principle used in
* createCompositionSourceDevice(). In most of the cases the paint
* device creation methods should be used instead of this function.
*
* \see createCompositionSourceDevice()
* \see createCompositionSourceDeviceFixed()
*/
virtual const KoColorSpace* compositionSourceColorSpace() const;
/**
* @return the internal datamanager that keeps the pixels.
*/
KisDataManagerSP dataManager() const;
/**
* Replace the pixel data, color strategy, and profile.
*/
void setDataManager(KisDataManagerSP data, const KoColorSpace * colorSpace = 0);
/**
* Return the number of bytes a pixel takes.
*/
quint32 pixelSize() const;
/**
* Return the number of channels a pixel takes
*/
quint32 channelCount() const;
public:
/**
* Add the specified rect to the parent layer's set of dirty rects
* (if there is a parent layer)
*/
void setDirty(const QRect & rc);
/**
* Add the specified region to the parent layer's dirty region
* (if there is a parent layer)
*/
void setDirty(const QRegion & region);
/**
* Set the parent layer completely dirty, if this paint device has
* as parent layer.
*/
void setDirty();
void setDirty(const QVector<QRect> rects);
public:
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w);
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const;
KisVLineIteratorSP createVLineIteratorNG(qint32 x, qint32 y, qint32 h);
KisVLineConstIteratorSP createVLineConstIteratorNG(qint32 x, qint32 y, qint32 h) const;
KisRandomAccessorSP createRandomAccessorNG(qint32 x, qint32 y);
KisRandomConstAccessorSP createRandomConstAccessorNG(qint32 x, qint32 y) const;
/**
* Create an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*
* @param rc indicates the rectangle that truly contains data
*/
KisRepeatHLineConstIteratorSP createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const;
/**
* Create an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*
* @param rc indicates the rectangle that trully contains data
*/
KisRepeatVLineConstIteratorSP createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const;
/**
* This function create a random accessor which can easily access to sub pixel values.
* @param selection an up-to-date selection that has the same origin as the paint device
*/
KisRandomSubAccessorSP createRandomSubAccessor() const;
/** Clear the selected pixels from the paint device */
void clearSelection(KisSelectionSP selection);
Q_SIGNALS:
void profileChanged(const KoColorProfile * profile);
void colorSpaceChanged(const KoColorSpace *colorspace);
public:
friend class PaintDeviceCache;
/**
* Caclculates exact bounds of the device. Used internally
* by a transparent caching system. The solution is very slow
* because it does a linear scanline search. So the complexity
* is n*n at worst.
*
* \see exactBounds(), nonDefaultPixelArea()
*/
QRect calculateExactBounds(bool nonDefaultOnly) const;
public:
struct MemoryReleaseObject : public QObject
{
~MemoryReleaseObject();
};
static MemoryReleaseObject* createMemoryReleaseObject();
private:
KisPaintDevice& operator=(const KisPaintDevice&);
void init(KisDataManagerSP explicitDataManager,
const KoColorSpace *colorSpace,
KisDefaultBoundsBaseSP defaultBounds,
KisNodeWSP parent, const QString& name);
// Only KisPainter is allowed to have access to these low-level methods
friend class KisPainter;
/**
* Return a vector with in order the size in bytes of the channels
* in the colorspace of this paint device.
*/
QVector<qint32> channelSizes() const;
protected:
friend class KisSelectionTest;
KisNodeWSP parentNode() const;
private:
struct Private;
Private * const m_d;
};
+class QRect;
+class QString;
+
+#include "kis_types.h"
+
+void KRITAIMAGE_EXPORT kis_debug_save_device_incremental(KisPaintDeviceSP device,
+ int i,
+ const QRect &rc,
+ const QString &suffix, const QString &prefix);
+
+/**
+ * Saves the paint device incrementally. Put this macro into a
+ * function that is called several times and you'll have as many
+ * separate dump files as the number of times the function was
+ * called. That is very convenient for debugging canvas updates:
+ * adding this macro will let you track the whole history of updates.
+ *
+ * The files are saved with pattern: <counter>_<suffix>.png
+ */
+#define KIS_DUMP_DEVICE_1(device, rc, suffix) \
+ do { \
+ static int i = -1; i++; \
+ kis_debug_save_device_incremental((device), i, (rc), (suffix), QString()); \
+ } while(0)
+
+/**
+ * Saves the paint device incrementally. Put this macro into a
+ * function that is called several times and you'll have as many
+ * separate dump files as the number of times the function was
+ * called. That is very convenient for debugging canvas updates:
+ * adding this macro will let you track the whole history of updates.
+ *
+ * The \p prefix parameter makes it easy to sort out dumps from
+ * different functions.
+ *
+ * The files are saved with pattern: <prefix>_<counter>_<suffix>.png
+ */
+#define KIS_DUMP_DEVICE_2(device, rc, suffix, prefix) \
+ do { \
+ static int i = -1; i++; \
+ kis_debug_save_device_incremental((device), i, (rc), (suffix), (prefix)); \
+ } while(0)
+
+
+
#endif // KIS_PAINT_DEVICE_IMPL_H_
diff --git a/krita/image/kis_painter.cc b/krita/image/kis_painter.cc
index 43717d512a2..eeb1569752f 100644
--- a/krita/image/kis_painter.cc
+++ b/krita/image/kis_painter.cc
@@ -1,2822 +1,2822 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara Toloza <pentalis@gmail.com>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_painter.h"
#include <stdlib.h>
#include <string.h>
#include <cfloat>
#include <cmath>
#include <climits>
#ifndef Q_OS_WIN
#include <strings.h>
#endif
#include <QImage>
#include <QRect>
#include <QString>
#include <QStringList>
#include <kundo2command.h>
#include <kis_debug.h>
#include <klocale.h>
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoCompositeOpRegistry.h>
#include "kis_image.h"
#include "filter/kis_filter.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_fixed_paint_device.h"
#include "kis_transaction.h"
#include "kis_types.h"
#include "kis_vec.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "kis_paintop.h"
#include "kis_selection.h"
#include "kis_fill_painter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_pixel_selection.h"
#include "kis_paint_information.h"
#include "kis_paintop_registry.h"
#include "kis_perspective_math.h"
#include "tiles3/kis_random_accessor.h"
// Maximum distance from a Bezier control point to the line through the start
// and end points for the curve to be considered flat.
#define BEZIER_FLATNESS_THRESHOLD 0.5
#define trunc(x) ((int)(x))
#ifndef Q_OS_WIN
#endif
struct Q_DECL_HIDDEN KisPainter::Private {
Private(KisPainter *_q) : q(_q) {}
KisPainter *q;
KisPaintDeviceSP device;
KisSelectionSP selection;
KisTransaction* transaction;
KoUpdater* progressUpdater;
QVector<QRect> dirtyRects;
KisPaintOp* paintOp;
KoColor paintColor;
KoColor backgroundColor;
const KisFilterConfiguration* generator;
KisPaintLayer* sourceLayer;
FillStyle fillStyle;
StrokeStyle strokeStyle;
bool antiAliasPolygonFill;
const KoPattern* pattern;
QPointF duplicateOffset;
quint32 pixelSize;
const KoColorSpace* colorSpace;
KoColorProfile* profile;
const KoCompositeOp* compositeOp;
const KoAbstractGradient* gradient;
KisPaintOpPresetSP paintOpPreset;
QImage polygonMaskImage;
QPainter* maskPainter;
KisFillPainter* fillPainter;
KisPaintDeviceSP polygon;
qint32 maskImageWidth;
qint32 maskImageHeight;
QPointF axesCenter;
bool mirrorHorizontaly;
bool mirrorVerticaly;
bool isOpacityUnit; // TODO: move into ParameterInfo
KoCompositeOp::ParameterInfo paramInfo;
KoColorConversionTransformation::Intent renderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags;
bool tryReduceSourceRect(const KisPaintDevice *srcDev,
QRect *srcRect,
qint32 *srcX,
qint32 *srcY,
qint32 *srcWidth,
qint32 *srcHeight,
qint32 *dstX,
qint32 *dstY);
void fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect);
};
KisPainter::KisPainter()
: d(new Private(this))
{
init();
}
KisPainter::KisPainter(KisPaintDeviceSP device)
: d(new Private(this))
{
init();
Q_ASSERT(device);
begin(device);
}
KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: d(new Private(this))
{
init();
Q_ASSERT(device);
begin(device);
d->selection = selection;
}
void KisPainter::init()
{
d->selection = 0 ;
d->transaction = 0;
d->paintOp = 0;
d->pattern = 0;
d->sourceLayer = 0;
d->fillStyle = FillStyleNone;
d->strokeStyle = StrokeStyleBrush;
d->antiAliasPolygonFill = true;
d->progressUpdater = 0;
d->gradient = 0;
d->maskPainter = 0;
d->fillPainter = 0;
d->maskImageWidth = 255;
d->maskImageHeight = 255;
d->mirrorHorizontaly = false;
d->mirrorVerticaly = false;
d->isOpacityUnit = true;
d->paramInfo = KoCompositeOp::ParameterInfo();
d->renderingIntent = KoColorConversionTransformation::InternalRenderingIntent;
d->conversionFlags = KoColorConversionTransformation::InternalConversionFlags;
}
KisPainter::~KisPainter()
{
// TODO: Maybe, don't be that strict?
// deleteTransaction();
end();
delete d->paintOp;
delete d->maskPainter;
delete d->fillPainter;
delete d;
}
template <bool useOldData>
void copyAreaOptimizedImpl(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
const QRect dstRect(dstPt, srcRect.size());
const bool srcEmpty = (src->extent() & srcRect).isEmpty();
const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
if (!srcEmpty || !dstEmpty) {
if (srcEmpty) {
dst->clear(dstRect);
} else {
KisPainter gc(dst);
gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
if (useOldData) {
gc.bitBltOldData(dstRect.topLeft(), src, srcRect);
} else {
gc.bitBlt(dstRect.topLeft(), src, srcRect);
}
}
}
}
void KisPainter::copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
copyAreaOptimizedImpl<false>(dstPt, src, dst, srcRect);
}
void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
copyAreaOptimizedImpl<true>(dstPt, src, dst, srcRect);
}
void KisPainter::copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &originalSrcRect,
KisSelectionSP selection)
{
if (!selection) {
copyAreaOptimized(dstPt, src, dst, originalSrcRect);
return;
}
const QRect selectionRect = selection->selectedRect();
const QRect srcRect = originalSrcRect & selectionRect;
const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft();
const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size());
const bool srcEmpty = (src->extent() & srcRect).isEmpty();
const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
if (!srcEmpty || !dstEmpty) {
//if (srcEmpty) {
// doesn't support dstRect
// dst->clearSelection(selection);
// } else */
{
KisPainter gc(dst);
gc.setSelection(selection);
gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
gc.bitBlt(dstRect.topLeft(), src, srcRect);
}
}
}
void KisPainter::begin(KisPaintDeviceSP device)
{
begin(device, d->selection);
}
void KisPainter::begin(KisPaintDeviceSP device, KisSelectionSP selection)
{
if (!device) return;
d->selection = selection;
Q_ASSERT(device->colorSpace());
end();
d->device = device;
d->colorSpace = device->colorSpace();
d->compositeOp = d->colorSpace->compositeOp(COMPOSITE_OVER);
d->pixelSize = device->pixelSize();
}
void KisPainter::end()
{
Q_ASSERT_X(!d->transaction, "KisPainter::end()",
"end() was called for the painter having a transaction. "
"Please use end/deleteTransaction() instead");
}
void KisPainter::beginTransaction(const KUndo2MagicString& transactionName,int timedID)
{
Q_ASSERT_X(!d->transaction, "KisPainter::beginTransaction()",
"You asked for a new transaction while still having "
"another one. Please finish the first one with "
"end/deleteTransaction() first");
d->transaction = new KisTransaction(transactionName, d->device);
Q_CHECK_PTR(d->transaction);
d->transaction->undoCommand()->setTimedID(timedID);
}
void KisPainter::revertTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::revertTransaction()",
"No transaction is in progress");
d->transaction->revert();
delete d->transaction;
d->transaction = 0;
}
void KisPainter::endTransaction(KisUndoAdapter *undoAdapter)
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
d->transaction->commit(undoAdapter);
delete d->transaction;
d->transaction = 0;
}
void KisPainter::endTransaction(KisPostExecutionUndoAdapter *undoAdapter)
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
d->transaction->commit(undoAdapter);
delete d->transaction;
d->transaction = 0;
}
KUndo2Command* KisPainter::endAndTakeTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
KUndo2Command *transactionData = d->transaction->endAndTake();
delete d->transaction;
d->transaction = 0;
return transactionData;
}
void KisPainter::deleteTransaction()
{
if (!d->transaction) return;
delete d->transaction;
d->transaction = 0;
}
void KisPainter::putTransaction(KisTransaction* transaction)
{
Q_ASSERT_X(!d->transaction, "KisPainter::putTransaction()",
"You asked for a new transaction while still having "
"another one. Please finish the first one with "
"end/deleteTransaction() first");
d->transaction = transaction;
}
KisTransaction* KisPainter::takeTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::takeTransaction()",
"No transaction is in progress");
KisTransaction *temp = d->transaction;
d->transaction = 0;
return temp;
}
QVector<QRect> KisPainter::takeDirtyRegion()
{
QVector<QRect> vrect = d->dirtyRects;
d->dirtyRects.clear();
return vrect;
}
void KisPainter::addDirtyRect(const QRect & rc)
{
QRect r = rc.normalized();
if (r.isValid()) {
d->dirtyRects.append(rc);
}
}
inline bool KisPainter::Private::tryReduceSourceRect(const KisPaintDevice *srcDev,
QRect *srcRect,
qint32 *srcX,
qint32 *srcY,
qint32 *srcWidth,
qint32 *srcHeight,
qint32 *dstX,
qint32 *dstY)
{
/**
* In case of COMPOSITE_COPY and Wrap Around Mode even the pixels
* outside the device extent matter, because they will be either
* directly copied (former case) or cloned from another area of
* the image.
*/
if (compositeOp->id() != COMPOSITE_COPY &&
!srcDev->defaultBounds()->wrapAroundMode()) {
/**
* If srcDev->extent() (the area of the tiles containing
* srcDev) is smaller than srcRect, then shrink srcRect to
* that size. This is done as a speed optimization, useful for
* stack recomposition in KisImage. srcRect won't grow if
* srcDev->extent() is larger.
*/
*srcRect &= srcDev->extent();
if (srcRect->isEmpty()) return true;
// Readjust the function paramenters to the new dimensions.
*dstX += srcRect->x() - *srcX; // This will only add, not subtract
*dstY += srcRect->y() - *srcY; // Idem
srcRect->getRect(srcX, srcY, srcWidth, srcHeight);
}
return false;
}
void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
// TODO: get selX and selY working as intended
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
// Check that selection has an alpha colorspace, crash if false
Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize YET as it would obfuscate the mistake. */
Q_ASSERT(selection->bounds().contains(selRect));
Q_UNUSED(selRect); // only used by the above Q_ASSERT
/**
* An optimization, which crops the source rect by the bounds of
* the source device when it is possible
*/
if (d->tryReduceSourceRect(srcDev, &srcRect,
&srcX, &srcY,
&srcWidth, &srcHeight,
&dstX, &dstY)) return;
/* Create an intermediate byte array to hold information before it is written
to the current paint device (d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "dst bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
// Copy the relevant bytes of raw data from srcDev
quint8* srcBytes = 0;
try {
srcBytes = new quint8[srcWidth * srcHeight * srcDev->pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "src bytes";
return;
}
srcDev->readBytes(srcBytes, srcX, srcY, srcWidth, srcHeight);
QRect selBounds = selection->bounds();
const quint8 *selRowStart = selection->data() +
(selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
/*
* This checks whether there is nothing selected.
*/
if (!d->selection) {
/* As there's nothing selected, blit to dstBytes (intermediary bit array),
ignoring d->selection (the user selection)*/
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcBytes;
d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize();
d->paramInfo.maskRowStart = selRowStart;
d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
}
else {
/* Read the user selection (d->selection) bytes into an array, ready
to merge in the next block*/
quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
quint8* mergedSelectionBytes = 0;
try {
mergedSelectionBytes = new quint8[ totalBytes ];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
// Merge selections here by multiplying them - compositeOP(COMPOSITE_MULT)
d->paramInfo.dstRowStart = mergedSelectionBytes;
d->paramInfo.dstRowStride = srcWidth * selection->pixelSize();
d->paramInfo.srcRowStart = selRowStart;
d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo);
// Blit to dstBytes (intermediary bit array)
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcBytes;
d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize();
d->paramInfo.maskRowStart = mergedSelectionBytes;
d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
delete[] mergedSelectionBytes;
}
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] dstBytes;
delete[] srcBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 srcWidth, qint32 srcHeight)
{
bitBltWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
}
template <bool useOldSrcData>
void KisPainter::bitBltImpl(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
if (d->compositeOp->id() == COMPOSITE_COPY) {
if(!d->selection && d->isOpacityUnit &&
srcX == dstX && srcY == dstY &&
d->device->fastBitBltPossible(srcDev)) {
if(useOldSrcData) {
d->device->fastBitBltOldData(srcDev, srcRect);
} else {
d->device->fastBitBlt(srcDev, srcRect);
}
addDirtyRect(srcRect);
return;
}
}
else {
/**
* An optimization, which crops the source rect by the bounds of
* the source device when it is possible
*/
if (d->tryReduceSourceRect(srcDev, &srcRect,
&srcX, &srcY,
&srcWidth, &srcHeight,
&dstX, &dstY)) return;
}
qint32 dstY_ = dstY;
qint32 srcY_ = srcY;
qint32 rowsRemaining = srcHeight;
// Read below
KisRandomConstAccessorSP srcIt = srcDev->createRandomConstAccessorNG(srcX, srcY);
KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(dstX, dstY);
/* Here be a huge block of verbose code that does roughly the same than
the other bit blit operations. This one is longer than the rest in an effort to
optimize speed and memory use */
if (d->selection) {
KisPaintDeviceSP selectionProjection = d->selection->projection();
KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(dstX, dstY);
while (rowsRemaining > 0) {
qint32 dstX_ = dstX;
qint32 srcX_ = srcX;
qint32 columnsRemaining = srcWidth;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY_);
qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
rows = qMin(rows, numContiguousSelRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX_);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, numContiguousSelColumns);
columns = qMin(columns, columnsRemaining);
qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
srcIt->moveTo(srcX_, srcY_);
qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
dstIt->moveTo(dstX_, dstY_);
qint32 maskRowStride = maskIt->rowStride(dstX_, dstY_);
maskIt->moveTo(dstX_, dstY_);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
// if we don't use the oldRawData, we need to access the rawData of the source device.
d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
d->paramInfo.srcRowStride = srcRowStride;
d->paramInfo.maskRowStart = static_cast<KisRandomAccessor2*>(maskIt.data())->rawData();
d->paramInfo.maskRowStride = maskRowStride;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
srcX_ += columns;
dstX_ += columns;
columnsRemaining -= columns;
}
srcY_ += rows;
dstY_ += rows;
rowsRemaining -= rows;
}
}
else {
while (rowsRemaining > 0) {
qint32 dstX_ = dstX;
qint32 srcX_ = srcX;
qint32 columnsRemaining = srcWidth;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, columnsRemaining);
qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
srcIt->moveTo(srcX_, srcY_);
qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
dstIt->moveTo(dstX_, dstY_);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
// if we don't use the oldRawData, we need to access the rawData of the source device.
d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
d->paramInfo.srcRowStride = srcRowStride;
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
srcX_ += columns;
dstX_ += columns;
columnsRemaining -= columns;
}
srcY_ += rows;
dstY_ += rows;
rowsRemaining -= rows;
}
}
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bitBlt(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
bitBltImpl<false>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
}
void KisPainter::bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
{
bitBlt(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::bitBltOldData(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
bitBltImpl<true>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
}
void KisPainter::bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
{
bitBltOldData(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
* initializing they perform some dummy passes with those parameters, and it must not crash */
if(width == 0 || height == 0 || d->device.isNull())
return;
KoColor srcColor(color, d->device->compositionSourceColorSpace());
qint32 dstY = y;
qint32 rowsRemaining = height;
KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(x, y);
if(d->selection) {
KisPaintDeviceSP selectionProjection = d->selection->projection();
KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(x, y);
while(rowsRemaining > 0) {
qint32 dstX = x;
qint32 columnsRemaining = width;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY);
qint32 rows = qMin(numContiguousDstRows, numContiguousSelRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSelColumns);
columns = qMin(columns, columnsRemaining);
qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
dstIt->moveTo(dstX, dstY);
qint32 maskRowStride = maskIt->rowStride(dstX, dstY);
maskIt->moveTo(dstX, dstY);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
d->paramInfo.srcRowStart = srcColor.data();
d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
d->paramInfo.maskRowStart = maskIt->oldRawData();
d->paramInfo.maskRowStride = maskRowStride;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
dstX += columns;
columnsRemaining -= columns;
}
dstY += rows;
rowsRemaining -= rows;
}
}
else {
while(rowsRemaining > 0) {
qint32 dstX = x;
qint32 columnsRemaining = width;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
qint32 rows = qMin(numContiguousDstRows, rowsRemaining);
while(columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
qint32 columns = qMin(numContiguousDstColumns, columnsRemaining);
qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
dstIt->moveTo(dstX, dstY);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
d->paramInfo.srcRowStart = srcColor.data();
d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
dstX += columns;
columnsRemaining -= columns;
}
dstY += rows;
rowsRemaining -= rows;
}
}
addDirtyRect(QRect(x, y, width, height));
}
void KisPainter::bltFixed(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect srcBounds = srcDev->bounds();
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
Q_ASSERT(srcBounds.contains(srcRect));
Q_UNUSED(srcRect); // only used in above assertion
/* Create an intermediate byte array to hold information before it is written
to the current paint device (aka: d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bltFixed std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
const quint8 *srcRowStart = srcDev->data() +
(srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
if (d->selection) {
/* d->selection is a KisPaintDevice, so first a readBytes is performed to
get the area of interest... */
KisPaintDeviceSP selectionProjection = d->selection->projection();
quint8* selBytes = 0;
try {
selBytes = new quint8[srcWidth * srcHeight * selectionProjection->pixelSize()];
}
catch (std::bad_alloc) {
delete[] dstBytes;
return;
}
selectionProjection->readBytes(selBytes, dstX, dstY, srcWidth, srcHeight);
d->paramInfo.maskRowStart = selBytes;
d->paramInfo.maskRowStride = srcWidth * selectionProjection->pixelSize();
}
// ...and then blit.
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] d->paramInfo.maskRowStart;
delete[] dstBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect)
{
bltFixed(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
quint32 srcWidth, quint32 srcHeight)
{
// TODO: get selX and selY working as intended
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
// Check that selection has an alpha colorspace, crash if false
Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
QRect srcBounds = srcDev->bounds();
QRect selBounds = selection->bounds();
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
Q_ASSERT(srcBounds.contains(srcRect));
Q_UNUSED(srcRect); // only used in above assertion
Q_ASSERT(selBounds.contains(selRect));
Q_UNUSED(selRect); // only used in above assertion
/* Create an intermediate byte array to hold information before it is written
to the current paint device (aka: d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
const quint8 *srcRowStart = srcDev->data() +
(srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
const quint8 *selRowStart = selection->data() +
(selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
if (!d->selection) {
/* As there's nothing selected, blit to dstBytes (intermediary bit array),
ignoring d->selection (the user selection)*/
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = selRowStart;
d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
}
else {
/* Read the user selection (d->selection) bytes into an array, ready
to merge in the next block*/
quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
quint8 * mergedSelectionBytes = 0;
try {
mergedSelectionBytes = new quint8[ totalBytes ];
} catch (std::bad_alloc) {
warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << totalBytes << "total bytes";
delete[] dstBytes;
return;
}
d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
// Merge selections here by multiplying them - compositeOp(COMPOSITE_MULT)
d->paramInfo.dstRowStart = mergedSelectionBytes;
d->paramInfo.dstRowStride = srcWidth * selection->pixelSize();
d->paramInfo.srcRowStart = selRowStart;
d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo);
// Blit to dstBytes (intermediary bit array)
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = mergedSelectionBytes;
d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
delete[] mergedSelectionBytes;
}
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] dstBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
quint32 srcWidth, quint32 srcHeight)
{
bltFixedWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
}
void KisPainter::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance)
{
if (d->device && d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintLine(pi1, pi2, currentDistance);
}
}
void KisPainter::paintPolyline(const vQPointF &points,
int index, int numPoints)
{
if (index >= (int) points.count())
return;
if (numPoints < 0)
numPoints = points.count();
if (index + numPoints > (int) points.count())
numPoints = points.count() - index;
KisDistanceInformation saveDist;
for (int i = index; i < index + numPoints - 1; i++) {
paintLine(points [i], points [i + 1], &saveDist);
}
}
static void getBezierCurvePoints(const KisVector2D &pos1,
const KisVector2D &control1,
const KisVector2D &control2,
const KisVector2D &pos2,
vQPointF& points)
{
LineEquation line = LineEquation::Through(pos1, pos2);
qreal d1 = line.absDistance(control1);
qreal d2 = line.absDistance(control2);
if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
points.push_back(toQPointF(pos1));
} else {
// Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
KisVector2D l2 = (pos1 + control1) / 2;
KisVector2D h = (control1 + control2) / 2;
KisVector2D l3 = (l2 + h) / 2;
KisVector2D r3 = (control2 + pos2) / 2;
KisVector2D r2 = (h + r3) / 2;
KisVector2D l4 = (l3 + r2) / 2;
getBezierCurvePoints(pos1, l2, l3, l4, points);
getBezierCurvePoints(l4, r2, r3, pos2, points);
}
}
void KisPainter::getBezierCurvePoints(const QPointF &pos1,
const QPointF &control1,
const QPointF &control2,
const QPointF &pos2,
vQPointF& points) const
{
::getBezierCurvePoints(toKisVector2D(pos1), toKisVector2D(control1), toKisVector2D(control2), toKisVector2D(pos2), points);
}
void KisPainter::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance)
{
if (d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintBezierCurve(pi1, control1, control2, pi2, currentDistance);
}
}
void KisPainter::paintRect(const QRectF &rect)
{
QRectF normalizedRect = rect.normalized();
vQPointF points;
points.push_back(normalizedRect.topLeft());
points.push_back(normalizedRect.bottomLeft());
points.push_back(normalizedRect.bottomRight());
points.push_back(normalizedRect.topRight());
paintPolygon(points);
}
void KisPainter::paintRect(const qreal x,
const qreal y,
const qreal w,
const qreal h)
{
paintRect(QRectF(x, y, w, h));
}
void KisPainter::paintEllipse(const QRectF &rect)
{
QRectF r = rect.normalized(); // normalize before checking as negative width and height are empty too
if (r.isEmpty()) return;
// See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
// kappa = (4/3*(sqrt(2)-1))
const qreal kappa = 0.5522847498;
const qreal lx = (r.width() / 2) * kappa;
const qreal ly = (r.height() / 2) * kappa;
QPointF center = r.center();
QPointF p0(r.left(), center.y());
QPointF p1(r.left(), center.y() - ly);
QPointF p2(center.x() - lx, r.top());
QPointF p3(center.x(), r.top());
vQPointF points;
getBezierCurvePoints(p0, p1, p2, p3, points);
QPointF p4(center.x() + lx, r.top());
QPointF p5(r.right(), center.y() - ly);
QPointF p6(r.right(), center.y());
getBezierCurvePoints(p3, p4, p5, p6, points);
QPointF p7(r.right(), center.y() + ly);
QPointF p8(center.x() + lx, r.bottom());
QPointF p9(center.x(), r.bottom());
getBezierCurvePoints(p6, p7, p8, p9, points);
QPointF p10(center.x() - lx, r.bottom());
QPointF p11(r.left(), center.y() + ly);
getBezierCurvePoints(p9, p10, p11, p0, points);
paintPolygon(points);
}
void KisPainter::paintEllipse(const qreal x,
const qreal y,
const qreal w,
const qreal h)
{
paintEllipse(QRectF(x, y, w, h));
}
void KisPainter::paintAt(const KisPaintInformation& pi,
KisDistanceInformation *savedDist)
{
if (d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintAt(pi, savedDist);
}
}
void KisPainter::fillPolygon(const vQPointF& points, FillStyle fillStyle)
{
if (points.count() < 3) {
return;
}
if (fillStyle == FillStyleNone) {
return;
}
QPainterPath polygonPath;
polygonPath.moveTo(points.at(0));
for (int pointIndex = 1; pointIndex < points.count(); pointIndex++) {
polygonPath.lineTo(points.at(pointIndex));
}
polygonPath.closeSubpath();
d->fillStyle = fillStyle;
fillPainterPath(polygonPath);
}
void KisPainter::paintPolygon(const vQPointF& points)
{
if (d->fillStyle != FillStyleNone) {
fillPolygon(points, d->fillStyle);
}
if (d->strokeStyle != StrokeStyleNone) {
if (points.count() > 1) {
KisDistanceInformation distance;
for (int i = 0; i < points.count() - 1; i++) {
paintLine(KisPaintInformation(points[i]), KisPaintInformation(points[i + 1]), &distance);
}
paintLine(points[points.count() - 1], points[0], &distance);
}
}
}
void KisPainter::paintPainterPath(const QPainterPath& path)
{
if (d->fillStyle != FillStyleNone) {
fillPainterPath(path);
}
if (d->strokeStyle == StrokeStyleNone) return;
QPointF lastPoint, nextPoint;
int elementCount = path.elementCount();
KisDistanceInformation saveDist;
for (int i = 0; i < elementCount; i++) {
QPainterPath::Element element = path.elementAt(i);
switch (element.type) {
case QPainterPath::MoveToElement:
lastPoint = QPointF(element.x, element.y);
break;
case QPainterPath::LineToElement:
nextPoint = QPointF(element.x, element.y);
paintLine(KisPaintInformation(lastPoint), KisPaintInformation(nextPoint), &saveDist);
lastPoint = nextPoint;
break;
case QPainterPath::CurveToElement:
nextPoint = QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y);
paintBezierCurve(KisPaintInformation(lastPoint),
QPointF(path.elementAt(i).x, path.elementAt(i).y),
QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
KisPaintInformation(nextPoint), &saveDist);
lastPoint = nextPoint;
break;
default:
continue;
}
}
}
void KisPainter::fillPainterPath(const QPainterPath& path)
{
fillPainterPath(path, QRect());
}
void KisPainter::fillPainterPath(const QPainterPath& path, const QRect &requestedRect)
{
if (d->mirrorHorizontaly || d->mirrorVerticaly) {
QTransform C1 = QTransform::fromTranslate(-d->axesCenter.x(), -d->axesCenter.y());
QTransform C2 = QTransform::fromTranslate(d->axesCenter.x(), d->axesCenter.y());
QTransform t;
QPainterPath newPath;
QRect newRect;
if (d->mirrorHorizontaly) {
t = C1 * QTransform::fromScale(-1,1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorVerticaly) {
t = C1 * QTransform::fromScale(1,-1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorHorizontaly && d->mirrorVerticaly) {
t = C1 * QTransform::fromScale(-1,-1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
}
d->fillPainterPathImpl(path, requestedRect);
}
void KisPainter::Private::fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect)
{
if (fillStyle == FillStyleNone) {
return;
}
// Fill the polygon bounding rectangle with the required contents then we'll
// create a mask for the actual polygon coverage.
if (!fillPainter) {
polygon = device->createCompositionSourceDevice();
fillPainter = new KisFillPainter(polygon);
} else {
polygon->clear();
}
Q_CHECK_PTR(polygon);
QRectF boundingRect = path.boundingRect();
QRect fillRect = boundingRect.toAlignedRect();
// Expand the rectangle to allow for anti-aliasing.
fillRect.adjust(-1, -1, 1, 1);
if (requestedRect.isValid()) {
fillRect &= requestedRect;
}
switch (fillStyle) {
default:
// Fall through
case FillStyleGradient:
// Currently unsupported, fall through
case FillStyleStrokes:
// Currently unsupported, fall through
warnImage << "Unknown or unsupported fill style in fillPolygon\n";
case FillStyleForegroundColor:
fillPainter->fillRect(fillRect, q->paintColor(), OPACITY_OPAQUE_U8);
break;
case FillStyleBackgroundColor:
fillPainter->fillRect(fillRect, q->backgroundColor(), OPACITY_OPAQUE_U8);
break;
case FillStylePattern:
if (pattern) { // if the user hasn't got any patterns installed, we shouldn't crash...
fillPainter->fillRect(fillRect, pattern);
}
break;
case FillStyleGenerator:
if (generator) { // if the user hasn't got any generators, we shouldn't crash...
fillPainter->fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), q->generator());
}
break;
}
if (polygonMaskImage.isNull() || (maskPainter == 0)) {
polygonMaskImage = QImage(maskImageWidth, maskImageHeight, QImage::Format_ARGB32_Premultiplied);
maskPainter = new QPainter(&polygonMaskImage);
maskPainter->setRenderHint(QPainter::Antialiasing, q->antiAliasPolygonFill());
}
// Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
const QColor black(Qt::black);
const QBrush brush(Qt::white);
for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += maskImageWidth) {
for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += maskImageHeight) {
polygonMaskImage.fill(black.rgb());
maskPainter->translate(-x, -y);
maskPainter->fillPath(path, brush);
maskPainter->translate(x, y);
qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, maskImageWidth);
qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, maskImageHeight);
KisHLineIteratorSP lineIt = polygon->createHLineIteratorNG(x, y, rectWidth);
quint8 tmp;
for (int row = y; row < y + rectHeight; row++) {
QRgb* line = reinterpret_cast<QRgb*>(polygonMaskImage.scanLine(row - y));
do {
tmp = qRed(line[lineIt->x() - x]);
polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
} while (lineIt->nextPixel());
lineIt->nextRow();
}
}
}
QRect bltRect = !requestedRect.isEmpty() ? requestedRect : fillRect;
q->bitBlt(bltRect.x(), bltRect.y(), polygon, bltRect.x(), bltRect.y(), bltRect.width(), bltRect.height());
}
void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen)
{
drawPainterPath(path, pen, QRect());
}
void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen, const QRect &requestedRect)
{
// we are drawing mask, it has to be white
// color of the path is given by paintColor()
Q_ASSERT(pen.color() == Qt::white);
if (!d->fillPainter) {
d->polygon = d->device->createCompositionSourceDevice();
d->fillPainter = new KisFillPainter(d->polygon);
} else {
d->polygon->clear();
}
Q_CHECK_PTR(d->polygon);
QRectF boundingRect = path.boundingRect();
QRect fillRect = boundingRect.toAlignedRect();
// take width of the pen into account
int penWidth = qRound(pen.widthF());
fillRect.adjust(-penWidth, -penWidth, penWidth, penWidth);
// Expand the rectangle to allow for anti-aliasing.
fillRect.adjust(-1, -1, 1, 1);
if (!requestedRect.isNull()) {
fillRect &= requestedRect;
}
d->fillPainter->fillRect(fillRect, paintColor(), OPACITY_OPAQUE_U8);
if (d->polygonMaskImage.isNull() || (d->maskPainter == 0)) {
d->polygonMaskImage = QImage(d->maskImageWidth, d->maskImageHeight, QImage::Format_ARGB32_Premultiplied);
d->maskPainter = new QPainter(&d->polygonMaskImage);
d->maskPainter->setRenderHint(QPainter::Antialiasing, antiAliasPolygonFill());
}
// Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
const QColor black(Qt::black);
QPen oldPen = d->maskPainter->pen();
d->maskPainter->setPen(pen);
for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += d->maskImageWidth) {
for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += d->maskImageHeight) {
d->polygonMaskImage.fill(black.rgb());
d->maskPainter->translate(-x, -y);
d->maskPainter->drawPath(path);
d->maskPainter->translate(x, y);
qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, d->maskImageWidth);
qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, d->maskImageHeight);
KisHLineIteratorSP lineIt = d->polygon->createHLineIteratorNG(x, y, rectWidth);
quint8 tmp;
for (int row = y; row < y + rectHeight; row++) {
QRgb* line = reinterpret_cast<QRgb*>(d->polygonMaskImage.scanLine(row - y));
do {
tmp = qRed(line[lineIt->x() - x]);
d->polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
} while (lineIt->nextPixel());
lineIt->nextRow();
}
}
}
d->maskPainter->setPen(oldPen);
QRect r = d->polygon->extent();
bitBlt(r.x(), r.y(), d->polygon, r.x(), r.y(), r.width(), r.height());
}
inline void KisPainter::compositeOnePixel(quint8 *dst, const KoColor &color)
{
d->paramInfo.dstRowStart = dst;
d->paramInfo.dstRowStride = 0;
d->paramInfo.srcRowStart = color.data();
d->paramInfo.srcRowStride = 0;
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = 1;
d->paramInfo.cols = 1;
d->colorSpace->bitBlt(color.colorSpace(), d->paramInfo, d->compositeOp,
d->renderingIntent,
d->conversionFlags);
}
/**/
void KisPainter::drawLine(const QPointF& start, const QPointF& end, qreal width, bool antialias){
int x1 = start.x();
int y1 = start.y();
int x2 = end.x();
int y2 = end.y();
if ((x2 == x1 ) && (y2 == y1)) return;
int dstX = x2-x1;
int dstY = y2-y1;
qreal uniC = dstX*y1 - dstY*x1;
qreal projectionDenominator = 1.0 / (pow((double)dstX, 2) + pow((double)dstY, 2));
qreal subPixel;
if (qAbs(dstX) > qAbs(dstY)){
subPixel = start.x() - x1;
}else{
subPixel = start.y() - y1;
}
qreal halfWidth = width * 0.5 + subPixel;
int W_ = qRound(halfWidth) + 1;
// save the state
int X1_ = x1;
int Y1_ = y1;
int X2_ = x2;
int Y2_ = y2;
if (x2<x1) qSwap(x1,x2);
if (y2<y1) qSwap(y1,y2);
qreal denominator = sqrt(pow((double)dstY,2) + pow((double)dstX,2));
if (denominator == 0.0) {
denominator = 1.0;
}
denominator = 1.0/denominator;
qreal projection,scanX,scanY,AA_;
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1);
}
for (int y = y1-W_; y < y2+W_ ; y++){
for (int x = x1-W_; x < x2+W_; x++){
projection = ( (x-X1_)* dstX + (y-Y1_)*dstY ) * projectionDenominator;
scanX = X1_ + projection * dstX;
scanY = Y1_ + projection * dstY;
if (((scanX < x1) || (scanX > x2)) || ((scanY < y1) || (scanY > y2))) {
AA_ = qMin( sqrt( pow((double)x - X1_, 2) + pow((double)y - Y1_, 2) ),
sqrt( pow((double)x - X2_, 2) + pow((double)y - Y2_, 2) ));
}else{
AA_ = qAbs(dstY*x - dstX*y + uniC) * denominator;
}
if (AA_>halfWidth) {
continue;
}
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x,y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
KoColor mycolor = d->paintColor;
if (antialias && AA_ > halfWidth-1.0) {
mycolor.colorSpace()->multiplyAlpha(mycolor.data(), 1.0 - (AA_-(halfWidth-1.0)), 1);
}
compositeOnePixel(accessor->rawData(), mycolor);
}
}
}
}
/**/
void KisPainter::drawLine(const QPointF & start, const QPointF & end)
{
drawThickLine(start, end, 1, 1);
}
void KisPainter::drawDDALine(const QPointF & start, const QPointF & end)
{
int x = int(start.x());
int y = int(start.y());
int x2 = int(end.x());
int y2 = int(end.y());
// Width and height of the line
int xd = x2 - x;
int yd = y2 - y;
float m = (float)yd / (float)xd;
float fx = x;
float fy = y;
int inc;
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x, y);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x, y);
}
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x,y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
if (fabs(m) > 1.0f) {
inc = (yd > 0) ? 1 : -1;
m = 1.0f / m;
m *= inc;
while (y != y2) {
y = y + inc;
fx = fx + m;
x = qRound(fx);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
}
} else {
inc = (xd > 0) ? 1 : -1;
m *= inc;
while (x != x2) {
x = x + inc;
fy = fy + m;
y = qRound(fy);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
}
}
}
void KisPainter::drawWobblyLine(const QPointF & start, const QPointF & end)
{
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(start.x(), start.y());
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(start.x(), start.y());
}
KoColor mycolor(d->paintColor);
int x1 = start.x();
int y1 = start.y();
int x2 = end.x();
int y2 = end.y();
// Width and height of the line
int xd = (x2 - x1);
int yd = (y2 - y1);
int x;
int y;
float fx = (x = x1);
float fy = (y = y1);
float m = (float)yd / (float)xd;
int inc;
if (fabs(m) > 1) {
inc = (yd > 0) ? 1 : -1;
m = 1.0f / m;
m *= inc;
while (y != y2) {
fx = fx + m;
y = y + inc;
x = qRound(fx);
float br1 = int(fx + 1) - fx;
float br2 = fx - (int)fx;
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br1));
compositeOnePixel(accessor->rawData(), mycolor);
}
accessor->moveTo(x + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(x + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br2));
compositeOnePixel(accessor->rawData(), mycolor);
}
}
} else {
inc = (xd > 0) ? 1 : -1;
m *= inc;
while (x != x2) {
fy = fy + m;
x = x + inc;
y = qRound(fy);
float br1 = int(fy + 1) - fy;
float br2 = fy - (int)fy;
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br1));
compositeOnePixel(accessor->rawData(), mycolor);
}
accessor->moveTo(x, y + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, y + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br2));
compositeOnePixel(accessor->rawData(), mycolor);
}
}
}
}
void KisPainter::drawWuLine(const QPointF & start, const QPointF & end)
{
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(start.x(), start.y());
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(start.x(), start.y());
}
KoColor lineColor(d->paintColor);
int x1 = start.x();
int y1 = start.y();
int x2 = end.x();
int y2 = end.y();
float grad, xd, yd;
float xgap, ygap, xend, yend, yf, xf;
float brightness1, brightness2;
int ix1, ix2, iy1, iy2;
quint8 c1, c2;
// gradient of line
xd = (x2 - x1);
yd = (y2 - y1);
if (yd == 0) {
/* Horizontal line */
int incr = (x1 < x2) ? 1 : -1;
ix1 = (int)x1;
ix2 = (int)x2;
iy1 = (int)y1;
while (ix1 != ix2) {
ix1 = ix1 + incr;
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), lineColor);
}
}
return;
}
if (xd == 0) {
/* Vertical line */
int incr = (y1 < y2) ? 1 : -1;
iy1 = (int)y1;
iy2 = (int)y2;
ix1 = (int)x1;
while (iy1 != iy2) {
iy1 = iy1 + incr;
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), lineColor);
}
}
return;
}
if (fabs(xd) > fabs(yd)) {
// horizontal line
// line have to be paint from left to right
if (x1 > x2) {
float tmp;
tmp = x1; x1 = x2; x2 = tmp;
tmp = y1; y1 = y2; y2 = tmp;
xd = (x2 - x1);
yd = (y2 - y1);
}
grad = yd / xd;
// nearest X,Y interger coordinates
xend = static_cast<int>(x1 + 0.5f);
yend = y1 + grad * (xend - x1);
xgap = invertFrac(x1 + 0.5f);
ix1 = static_cast<int>(xend);
iy1 = static_cast<int>(yend);
// calc the intensity of the other end point pixel pair.
brightness1 = invertFrac(yend) * xgap;
brightness2 = frac(yend) * xgap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix1, iy1 + 1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1 + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// calc first Y-intersection for main loop
yf = yend + grad;
xend = trunc(x2 + 0.5f);
yend = y2 + grad * (xend - x2);
xgap = invertFrac(x2 - 0.5f);
ix2 = static_cast<int>(xend);
iy2 = static_cast<int>(yend);
brightness1 = invertFrac(yend) * xgap;
brightness2 = frac(yend) * xgap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix2, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix2, iy2 + 1);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2 + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// main loop
for (int x = ix1 + 1; x <= ix2 - 1; x++) {
brightness1 = invertFrac(yf);
brightness2 = frac(yf);
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(x, int (yf));
if (selectionAccessor) selectionAccessor->moveTo(x, int (yf));
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(x, int (yf) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, int (yf) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
yf = yf + grad;
}
} else {
//vertical
// line have to be painted from left to right
if (y1 > y2) {
float tmp;
tmp = x1; x1 = x2; x2 = tmp;
tmp = y1; y1 = y2; y2 = tmp;
xd = (x2 - x1);
yd = (y2 - y1);
}
grad = xd / yd;
// nearest X,Y interger coordinates
yend = static_cast<int>(y1 + 0.5f);
xend = x1 + grad * (yend - y1);
ygap = invertFrac(y1 + 0.5f);
ix1 = static_cast<int>(xend);
iy1 = static_cast<int>(yend);
// calc the intensity of the other end point pixel pair.
brightness1 = invertFrac(xend) * ygap;
brightness2 = frac(xend) * ygap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(x1 + 1, y1);
if (selectionAccessor) selectionAccessor->moveTo(x1 + 1, y1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// calc first Y-intersection for main loop
xf = xend + grad;
yend = trunc(y2 + 0.5f);
xend = x2 + grad * (yend - y2);
ygap = invertFrac(y2 - 0.5f);
ix2 = static_cast<int>(xend);
iy2 = static_cast<int>(yend);
brightness1 = invertFrac(xend) * ygap;
brightness2 = frac(xend) * ygap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix2, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix2 + 1, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2 + 1, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// main loop
for (int y = iy1 + 1; y <= iy2 - 1; y++) {
brightness1 = invertFrac(xf);
brightness2 = frac(xf);
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(int (xf), y);
if (selectionAccessor) selectionAccessor->moveTo(int (xf), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(int (xf) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(int (xf) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
xf = xf + grad;
}
}//end-of-else
}
void KisPainter::drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth)
{
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(start.x(), start.y());
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(start.x(), start.y());
}
const KoColorSpace *cs = d->device->colorSpace();
KoColor c1(d->paintColor);
KoColor c2(d->paintColor);
KoColor c3(d->paintColor);
KoColor col1(c1);
KoColor col2(c1);
float grada, gradb, dxa, dxb, dya, dyb, fraca, fracb,
xfa, yfa, xfb, yfb, b1a, b2a, b1b, b2b, dstX, dstY;
int x, y, ix1, ix2, iy1, iy2;
int x0a, y0a, x1a, y1a, x0b, y0b, x1b, y1b;
int tp0, tn0, tp1, tn1;
int horizontal = 0;
float opacity = 1.0;
tp0 = startWidth / 2;
tn0 = startWidth / 2;
if (startWidth % 2 == 0) // even width startWidth
tn0--;
tp1 = endWidth / 2;
tn1 = endWidth / 2;
if (endWidth % 2 == 0) // even width endWidth
tn1--;
int x0 = qRound(start.x());
int y0 = qRound(start.y());
int x1 = qRound(end.x());
int y1 = qRound(end.y());
dstX = x1 - x0; // run of general line
dstY = y1 - y0; // rise of general line
if (dstY < 0) dstY = -dstY;
if (dstX < 0) dstX = -dstX;
if (dstX > dstY) { // horizontalish
horizontal = 1;
x0a = x0; y0a = y0 - tn0;
x0b = x0; y0b = y0 + tp0;
x1a = x1; y1a = y1 - tn1;
x1b = x1; y1b = y1 + tp1;
} else {
x0a = x0 - tn0; y0a = y0;
x0b = x0 + tp0; y0b = y0;
x1a = x1 - tn1; y1a = y1;
x1b = x1 + tp1; y1b = y1;
}
if (horizontal) { // draw endpoints
for (int i = y0a; i <= y0b; i++) {
accessor->moveTo(x0, i);
if (selectionAccessor) selectionAccessor->moveTo(x0, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
for (int i = y1a; i <= y1b; i++) {
accessor->moveTo(x1, i);
if (selectionAccessor) selectionAccessor->moveTo(x1, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
} else {
for (int i = x0a; i <= x0b; i++) {
accessor->moveTo(i, y0);
if (selectionAccessor) selectionAccessor->moveTo(i, y0);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
for (int i = x1a; i <= x1b; i++) {
accessor->moveTo(i, y1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
}
//antialias endpoints
if (x1 != x0 && y1 != y0) {
if (horizontal) {
accessor->moveTo(x0a, y0a - 1);
if (selectionAccessor) selectionAccessor->moveTo(x0a, y0a - 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
accessor->moveTo(x1b, y1b + 1);
if (selectionAccessor) selectionAccessor->moveTo(x1b, y1b + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
} else {
accessor->moveTo(x0a - 1, y0a);
if (selectionAccessor) selectionAccessor->moveTo(x0a - 1, y0a);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
accessor->moveTo(x1b + 1, y1b);
if (selectionAccessor) selectionAccessor->moveTo(x1b + 1, y1b);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
}
dxa = x1a - x0a; // run of a
dya = y1a - y0a; // rise of a
dxb = x1b - x0b; // run of b
dyb = y1b - y0b; // rise of b
if (horizontal) { // horizontal-ish lines
if (x1 < x0) {
int xt, yt, wt;
KoColor tmp;
xt = x1a; x1a = x0a; x0a = xt;
yt = y1a; y1a = y0a; y0a = yt;
xt = x1b; x1b = x0b; x0b = xt;
yt = y1b; y1b = y0b; y0b = yt;
xt = x1; x1 = x0; x0 = xt;
yt = y1; y1 = y0; y0 = yt;
tmp = c1; c1 = c2; c2 = tmp;
wt = startWidth; startWidth = endWidth; endWidth = wt;
}
grada = dya / dxa;
gradb = dyb / dxb;
ix1 = x0; iy1 = y0;
ix2 = x1; iy2 = y1;
yfa = y0a + grada;
yfb = y0b + gradb;
for (x = ix1 + 1; x <= ix2 - 1; x++) {
fraca = yfa - int (yfa);
b1a = 1 - fraca;
b2a = fraca;
fracb = yfb - int (yfb);
b1b = 1 - fracb;
b2b = fracb;
// color first pixel of bottom line
opacity = ((x - ix1) / dstX) * c2.opacityF() + (1 - (x - ix1) / dstX) * c1.opacityF();
c3.setOpacity(opacity);
accessor->moveTo(x, (int)yfa);
if (selectionAccessor) selectionAccessor->moveTo(x, (int)yfa);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
// color first pixel of top line
if (!(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(x, (int)yfb);
if (selectionAccessor) selectionAccessor->moveTo(x, (int)yfb);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
// color second pixel of bottom line
if (grada != 0 && grada != 1) { // if not flat or exact diagonal
accessor->moveTo(x, int (yfa) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, int (yfa) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// color second pixel of top line
if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(x, int (yfb) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, int (yfb) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// fill remaining pixels
if (!(startWidth == 1 && endWidth == 1)) {
if (yfa < yfb)
for (int i = yfa + 1; i <= yfb; i++) {
accessor->moveTo(x, i);
if (selectionAccessor) selectionAccessor->moveTo(x, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
else
for (int i = yfa + 1; i >= yfb; i--) {
accessor->moveTo(x, i);
if (selectionAccessor) selectionAccessor->moveTo(x, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
}
yfa += grada;
yfb += gradb;
}
} else { // vertical-ish lines
if (y1 < y0) {
int xt, yt, wt;
xt = x1a; x1a = x0a; x0a = xt;
yt = y1a; y1a = y0a; y0a = yt;
xt = x1b; x1b = x0b; x0b = xt;
yt = y1b; y1b = y0b; y0b = yt;
xt = x1; x1 = x0; x0 = xt;
yt = y1; y1 = y0; y0 = yt;
KoColor tmp;
tmp = c1; c1 = c2; c2 = tmp;
wt = startWidth; startWidth = endWidth; endWidth = wt;
}
grada = dxa / dya;
gradb = dxb / dyb;
ix1 = x0; iy1 = y0;
ix2 = x1; iy2 = y1;
xfa = x0a + grada;
xfb = x0b + gradb;
for (y = iy1 + 1; y <= iy2 - 1; y++) {
fraca = xfa - int (xfa);
b1a = 1 - fraca;
b2a = fraca;
fracb = xfb - int (xfb);
b1b = 1 - fracb;
b2b = fracb;
// color first pixel of left line
opacity = ((y - iy1) / dstY) * c2.opacityF() + (1 - (y - iy1) / dstY) * c1.opacityF();
c3.setOpacity(opacity);
accessor->moveTo(int (xfa), y);
if (selectionAccessor) selectionAccessor->moveTo(int (xfa), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
// color first pixel of right line
if (!(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(int(xfb), y);
if (selectionAccessor) selectionAccessor->moveTo(int(xfb), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
// color second pixel of left line
if (grada != 0 && grada != 1) { // if not flat or exact diagonal
accessor->moveTo(int(xfa) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(int(xfa) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// color second pixel of right line
if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(int(xfb) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(int(xfb) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// fill remaining pixels between current xfa,xfb
if (!(startWidth == 1 && endWidth == 1)) {
if (xfa < xfb)
for (int i = (int) xfa + 1; i <= (int) xfb; i++) {
accessor->moveTo(i, y);
if (selectionAccessor) selectionAccessor->moveTo(i, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
else
for (int i = (int) xfb; i <= (int) xfa + 1; i++) {
accessor->moveTo(i, y);
if (selectionAccessor) selectionAccessor->moveTo(i, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
}
xfa += grada;
xfb += gradb;
}
}
}
void KisPainter::setProgress(KoUpdater * progressUpdater)
{
d->progressUpdater = progressUpdater;
}
const KisPaintDeviceSP KisPainter::device() const
{
return d->device;
}
KisPaintDeviceSP KisPainter::device()
{
return d->device;
}
void KisPainter::setChannelFlags(QBitArray channelFlags)
{
Q_ASSERT(channelFlags.isEmpty() || quint32(channelFlags.size()) == d->colorSpace->channelCount());
// Now, if all bits in the channelflags are true, pass an empty channel flags bitarray
// because otherwise the compositeops cannot optimize.
d->paramInfo.channelFlags = channelFlags;
if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) {
d->paramInfo.channelFlags = QBitArray();
}
}
QBitArray KisPainter::channelFlags()
{
return d->paramInfo.channelFlags;
}
void KisPainter::setPattern(const KoPattern * pattern)
{
d->pattern = pattern;
}
const KoPattern * KisPainter::pattern() const
{
return d->pattern;
}
void KisPainter::setPaintColor(const KoColor& color)
{
d->paintColor = color;
if (d->device) {
d->paintColor.convertTo(d->device->compositionSourceColorSpace());
}
}
const KoColor &KisPainter::paintColor() const
{
return d->paintColor;
}
void KisPainter::setBackgroundColor(const KoColor& color)
{
d->backgroundColor = color;
if (d->device) {
d->backgroundColor.convertTo(d->device->compositionSourceColorSpace());
}
}
const KoColor &KisPainter::backgroundColor() const
{
return d->backgroundColor;
}
void KisPainter::setGenerator(const KisFilterConfiguration * generator)
{
d->generator = generator;
}
const KisFilterConfiguration * KisPainter::generator() const
{
return d->generator;
}
void KisPainter::setFillStyle(FillStyle fillStyle)
{
d->fillStyle = fillStyle;
}
KisPainter::FillStyle KisPainter::fillStyle() const
{
return d->fillStyle;
}
void KisPainter::setAntiAliasPolygonFill(bool antiAliasPolygonFill)
{
d->antiAliasPolygonFill = antiAliasPolygonFill;
}
bool KisPainter::antiAliasPolygonFill()
{
return d->antiAliasPolygonFill;
}
void KisPainter::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
{
d->strokeStyle = strokeStyle;
}
KisPainter::StrokeStyle KisPainter::strokeStyle() const
{
return d->strokeStyle;
}
void KisPainter::setFlow(quint8 flow)
{
d->paramInfo.flow = float(flow) / 255.0f;
}
quint8 KisPainter::flow() const
{
return quint8(d->paramInfo.flow * 255.0f);
}
void KisPainter::setOpacityUpdateAverage(quint8 opacity)
{
d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f);
}
void KisPainter::setOpacity(quint8 opacity)
{
d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
d->paramInfo.opacity = float(opacity) / 255.0f;
}
quint8 KisPainter::opacity() const
{
return quint8(d->paramInfo.opacity * 255.0f);
}
void KisPainter::setCompositeOp(const KoCompositeOp * op)
{
d->compositeOp = op;
}
const KoCompositeOp * KisPainter::compositeOp()
{
return d->compositeOp;
}
void KisPainter::setCompositeOp(const QString& op)
{
d->compositeOp = d->colorSpace->compositeOp(op);
}
void KisPainter::setSelection(KisSelectionSP selection)
{
d->selection = selection;
}
KisSelectionSP KisPainter::selection()
{
return d->selection;
}
KoUpdater * KisPainter::progressUpdater()
{
return d->progressUpdater;
}
void KisPainter::setGradient(const KoAbstractGradient* gradient)
{
d->gradient = gradient;
}
const KoAbstractGradient* KisPainter::gradient() const
{
return d->gradient;
}
void KisPainter::setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image)
{
d->paintOpPreset = preset;
KisPaintOp *paintop = KisPaintOpRegistry::instance()->paintOp(preset, this, node, image);
Q_ASSERT(paintop);
if (paintop) {
delete d->paintOp;
d->paintOp = paintop;
}
else {
- qWarning() << "Could not create paintop for preset " << preset->name();
+ warnKrita << "Could not create paintop for preset " << preset->name();
}
}
KisPaintOpPresetSP KisPainter::preset() const
{
return d->paintOpPreset;
}
KisPaintOp* KisPainter::paintOp() const
{
return d->paintOp;
}
void KisPainter::setMirrorInformation(const QPointF& axesCenter, bool mirrorHorizontaly, bool mirrorVerticaly)
{
d->axesCenter = axesCenter;
d->mirrorHorizontaly = mirrorHorizontaly;
d->mirrorVerticaly = mirrorVerticaly;
}
void KisPainter::copyMirrorInformation(KisPainter* painter)
{
painter->setMirrorInformation(d->axesCenter, d->mirrorHorizontaly, d->mirrorVerticaly);
}
bool KisPainter::hasMirroring() const
{
return d->mirrorHorizontaly || d->mirrorVerticaly;
}
void KisPainter::setMaskImageSize(qint32 width, qint32 height)
{
d->maskImageWidth = qBound(1, width, 256);
d->maskImageHeight = qBound(1, height, 256);
d->fillPainter = 0;
d->polygonMaskImage = QImage();
}
//void KisPainter::setLockAlpha(bool protect)
//{
// if(d->paramInfo.channelFlags.isEmpty()) {
// d->paramInfo.channelFlags = d->colorSpace->channelFlags(true, true);
// }
// QBitArray switcher =
// d->colorSpace->channelFlags(protect, !protect);
// if(protect) {
// d->paramInfo.channelFlags &= switcher;
// }
// else {
// d->paramInfo.channelFlags |= switcher;
// }
// Q_ASSERT(quint32(d->paramInfo.channelFlags.size()) == d->colorSpace->channelCount());
//}
//bool KisPainter::alphaLocked() const
//{
// QBitArray switcher = d->colorSpace->channelFlags(false, true);
// return !(d->paramInfo.channelFlags & switcher).count(true);
//}
void KisPainter::setRenderingIntent(KoColorConversionTransformation::Intent intent)
{
d->renderingIntent = intent;
}
void KisPainter::setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags)
{
d->conversionFlags = conversionFlags;
}
void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab)
{
if (!d->mirrorHorizontaly && !d->mirrorVerticaly) return;
KisFixedPaintDeviceSP dabToProcess = dab;
if (preserveDab) {
dabToProcess = new KisFixedPaintDevice(*dab);
}
renderMirrorMask(rc, dabToProcess);
}
void KisPainter::renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask)
{
if (!d->mirrorHorizontaly && !d->mirrorVerticaly) return;
KisFixedPaintDeviceSP maskToProcess = mask;
if (preserveMask) {
maskToProcess = new KisFixedPaintDevice(*mask);
}
renderMirrorMask(rc, dab, sx, sy, maskToProcess);
}
void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab)
{
int x = rc.topLeft().x();
int y = rc.topLeft().y();
int mirrorX = -((x+rc.width()) - d->axesCenter.x()) + d->axesCenter.x();
int mirrorY = -((y+rc.height()) - d->axesCenter.y()) + d->axesCenter.y();
if (d->mirrorHorizontaly && d->mirrorVerticaly){
dab->mirror(true, false);
bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
dab->mirror(false,true);
bltFixed(mirrorX, mirrorY, dab, 0,0,rc.width(),rc.height());
dab->mirror(true, false);
bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
}
else if (d->mirrorHorizontaly){
dab->mirror(true, false);
bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
}
else if (d->mirrorVerticaly){
dab->mirror(false, true);
bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
}
}
void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask)
{
int x = rc.topLeft().x();
int y = rc.topLeft().y();
int mirrorX = -((x+rc.width()) - d->axesCenter.x()) + d->axesCenter.x();
int mirrorY = -((y+rc.height()) - d->axesCenter.y()) + d->axesCenter.y();
if (d->mirrorHorizontaly && d->mirrorVerticaly){
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
dab->mirror(false,true);
mask->mirror(false, true);
bltFixedWithFixedSelection(mirrorX,mirrorY, dab, mask, rc.width() ,rc.height() );
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
}else if (d->mirrorHorizontaly){
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
}else if (d->mirrorVerticaly){
dab->mirror(false, true);
mask->mirror(false, true);
bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
}
}
void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab){
if (d->mirrorHorizontaly || d->mirrorVerticaly){
KisFixedPaintDeviceSP mirrorDab = new KisFixedPaintDevice(dab->colorSpace());
QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
mirrorDab->setRect(dabRc);
mirrorDab->initialize();
dab->readBytes(mirrorDab->data(),rc);
renderMirrorMask( QRect(rc.topLeft(),dabRc.size()), mirrorDab);
}
}
void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask)
{
if (d->mirrorHorizontaly || d->mirrorVerticaly){
KisFixedPaintDeviceSP mirrorDab = new KisFixedPaintDevice(dab->colorSpace());
QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
mirrorDab->setRect(dabRc);
mirrorDab->initialize();
dab->readBytes(mirrorDab->data(),QRect(QPoint(sx,sy),rc.size()));
renderMirrorMask(rc, mirrorDab, mask);
}
}
void KisPainter::renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab)
{
QVector<QRect> rects;
int x = rc.topLeft().x();
int y = rc.topLeft().y();
int mirrorX = -((x+rc.width()) - d->axesCenter.x()) + d->axesCenter.x();
int mirrorY = -((y+rc.height()) - d->axesCenter.y()) + d->axesCenter.y();
rects << rc;
if (d->mirrorHorizontaly && d->mirrorVerticaly){
rects << QRect(mirrorX, y, rc.width(), rc.height());
rects << QRect(mirrorX, mirrorY, rc.width(), rc.height());
rects << QRect(x, mirrorY, rc.width(), rc.height());
} else if (d->mirrorHorizontaly) {
rects << QRect(mirrorX, y, rc.width(), rc.height());
} else if (d->mirrorVerticaly) {
rects << QRect(x, mirrorY, rc.width(), rc.height());
}
foreach(const QRect &rc, rects) {
d->device->clear(rc);
}
QRect resultRect = dab->extent() | rc;
bool intersects = false;
for (int i = 1; i < rects.size(); i++) {
if (rects[i].intersects(resultRect)) {
intersects = true;
break;
}
}
/**
* If there are no cross-intersections, we can use a fast path
* and do no cycling recompositioning
*/
if (!intersects) {
rects.resize(1);
}
foreach(const QRect &rc, rects) {
bitBlt(rc.topLeft(), dab, rc);
}
foreach(const QRect &rc, rects) {
renderMirrorMask(rc, dab);
}
}
diff --git a/krita/image/kis_polygonal_gradient_shape_strategy.cpp b/krita/image/kis_polygonal_gradient_shape_strategy.cpp
index 1db616b7570..c68c49db16c 100644
--- a/krita/image/kis_polygonal_gradient_shape_strategy.cpp
+++ b/krita/image/kis_polygonal_gradient_shape_strategy.cpp
@@ -1,415 +1,415 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_polygonal_gradient_shape_strategy.h"
#include "kis_debug.h"
#include "kis_algebra_2d.h"
#include <config-gsl.h>
#ifdef HAVE_GSL
#include <gsl/gsl_multimin.h>
#endif /* HAVE_GSL */
#include <QtCore/qmath.h>
#include <limits>
#include <boost/math/distributions/normal.hpp>
#include <QPainterPath>
#include "krita_utils.h"
namespace Private {
QPointF centerFromPath(const QPainterPath &selectionPath) {
QPointF center;
int numPoints = 0;
for (int i = 0; i < selectionPath.elementCount(); i++) {
QPainterPath::Element element = selectionPath.elementAt(i);
if (element.type == QPainterPath::CurveToDataElement) continue;
center += element;
numPoints++;
}
center /= numPoints;
return center;
}
qreal getDisnormedGradientValue(const QPointF &pt, const QPainterPath &selectionPath, qreal exponent)
{
// FIXME: exponent = 2.0
// We explicitly use pow2() and sqrt() functions here
// for efficiency reasons.
KIS_ASSERT_RECOVER_NOOP(qFuzzyCompare(exponent, 2.0));
const qreal minHiLevel = std::pow(0.5, 1.0 / exponent);
qreal ptWeightNode = 0.0;
for (int i = 0; i < selectionPath.elementCount(); i++) {
if (selectionPath.elementAt(i).isMoveTo()) continue;
const int prevI = i > 0 ? i - 1 : selectionPath.elementCount() - 1;
const QPointF edgeP1 = selectionPath.elementAt(prevI);
const QPointF edgeP2 = selectionPath.elementAt(i);
const QPointF edgeVec = edgeP1 - edgeP2;
const QPointF q1 = pt - edgeP1;
const QPointF q2 = pt - edgeP2;
const qreal proj1 = KisAlgebra2D::dotProduct(edgeVec, q1);
const qreal proj2 = KisAlgebra2D::dotProduct(edgeVec, q2);
qreal hi = 1.0;
// pt's projection lays outside the edge itself,
// when the projections has the same sign
if (proj1 * proj2 >= 0) {
QPointF nearestPointVec =
qAbs(proj1) < qAbs(proj2) ? q1 : q2;
hi = KisAlgebra2D::norm(nearestPointVec);
} else {
QLineF line(edgeP1, edgeP2);
hi = kisDistanceToLine(pt, line);
}
hi = qMax(minHiLevel, hi);
// disabled for efficiency reasons
// ptWeightNode += 1.0 / std::pow(hi, exponent);
ptWeightNode += 1.0 / pow2(hi);
}
// disabled for efficiency reasons
// return 1.0 / std::pow(ptWeightNode, 1.0 / exponent);
return 1.0 / std::sqrt(ptWeightNode);
}
qreal initialExtremumValue(bool searchForMax) {
return searchForMax ?
std::numeric_limits<qreal>::min() :
std::numeric_limits<qreal>::max();
}
bool findBestStartingPoint(int numSamples, const QPainterPath &path,
qreal exponent, bool searchForMax,
qreal initialExtremumValue,
QPointF *result) {
const qreal minStepThreshold = 0.3;
const int numCandidatesThreshold = 4;
KIS_ASSERT_RECOVER(numSamples >= 4) {
*result = centerFromPath(path);
return true;
}
int totalSamples = numSamples;
int effectiveSamples = numSamples & 0x1 ?
(numSamples - 1) / 2 + 1 : numSamples;
QRectF rect = path.boundingRect();
qreal xOffset = rect.width() / (totalSamples + 1);
qreal xStep = effectiveSamples == totalSamples ? xOffset : 2 * xOffset;
qreal yOffset = rect.height() / (totalSamples + 1);
qreal yStep = effectiveSamples == totalSamples ? yOffset : 2 * yOffset;
if (xStep < minStepThreshold || yStep < minStepThreshold) {
return false;
}
int numFound = 0;
int numCandidates = 0;
QPointF extremumPoint;
qreal extremumValue = initialExtremumValue;
const qreal eps = 1e-3;
int sanityNumRows = 0;
for (qreal y = rect.y() + yOffset; y < rect.bottom() - eps; y += yStep) {
int sanityNumColumns = 0;
sanityNumRows++;
for (qreal x = rect.x() + xOffset; x < rect.right() - eps; x += xStep) {
sanityNumColumns++;
const QPointF pt(x, y);
if (!path.contains(pt)) continue;
qreal value = getDisnormedGradientValue(pt, path, exponent);
bool isExtremum = searchForMax ? value > extremumValue : value < extremumValue;
numCandidates++;
if (isExtremum) {
numFound++;
extremumPoint = pt;
extremumValue = value;
}
}
KIS_ASSERT_RECOVER_NOOP(sanityNumColumns == effectiveSamples);
}
KIS_ASSERT_RECOVER_NOOP(sanityNumRows == effectiveSamples);
bool success = numFound && numCandidates >= numCandidatesThreshold;
if (success) {
*result = extremumPoint;
} else {
int newNumSamples = 2 * numSamples + 1;
success = findBestStartingPoint(newNumSamples, path,
exponent, searchForMax,
extremumValue,
result);
if (!success && numFound > 0) {
*result = extremumPoint;
success = true;
}
}
return success;
}
#ifdef HAVE_GSL
struct GradientDescentParams {
QPainterPath selectionPath;
qreal exponent;
bool searchForMax;
};
double errorFunc (const gsl_vector * x, void *paramsPtr)
{
double vX = gsl_vector_get(x, 0);
double vY = gsl_vector_get(x, 1);
const GradientDescentParams *params =
static_cast<const GradientDescentParams*>(paramsPtr);
qreal weight = getDisnormedGradientValue(QPointF(vX, vY),
params->selectionPath,
params->exponent);
return params->searchForMax ? 1.0 / weight : weight;
}
qreal calculateMaxWeight(const QPainterPath &selectionPath,
qreal exponent,
bool searchForMax)
{
const gsl_multimin_fminimizer_type *T =
gsl_multimin_fminimizer_nmsimplex2;
gsl_multimin_fminimizer *s = NULL;
gsl_vector *ss, *x;
gsl_multimin_function minex_func;
size_t iter = 0;
int status;
double size;
QPointF center;
bool centerExists =
findBestStartingPoint(4, selectionPath,
exponent, searchForMax,
Private::initialExtremumValue(searchForMax),
&center);
if (!centerExists || !selectionPath.contains(center)) {
// if the path is too small just return default values
if (selectionPath.boundingRect().width() >= 2.0 &&
selectionPath.boundingRect().height() >= 2.0) {
KIS_ASSERT_RECOVER_NOOP(selectionPath.contains(center));
}
return searchForMax ? 1.0 : 0.0;
}
/* Starting point */
x = gsl_vector_alloc (2);
gsl_vector_set (x, 0, center.x());
gsl_vector_set (x, 1, center.y());
/* Set initial step sizes to 10 px */
ss = gsl_vector_alloc (2);
gsl_vector_set (ss, 0, 10);
gsl_vector_set (ss, 1, 10);
GradientDescentParams p;
p.selectionPath = selectionPath;
p.exponent = exponent;
p.searchForMax = searchForMax;
/* Initialize method and iterate */
minex_func.n = 2;
minex_func.f = errorFunc;
minex_func.params = (void*)&p;
s = gsl_multimin_fminimizer_alloc (T, 2);
gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
qreal result = searchForMax ?
getDisnormedGradientValue(center, selectionPath, exponent) : 0.0;
do
{
iter++;
status = gsl_multimin_fminimizer_iterate(s);
if (status)
break;
size = gsl_multimin_fminimizer_size (s);
status = gsl_multimin_test_size (size, 1e-6);
if (status == GSL_SUCCESS)
{
- // qDebug() << "*******Converged to minimum";
- // qDebug() << gsl_vector_get (s->x, 0)
+ // dbgKrita << "*******Converged to minimum";
+ // dbgKrita << gsl_vector_get (s->x, 0)
// << gsl_vector_get (s->x, 1)
// << "|" << s->fval << size;
result = searchForMax ? 1.0 / s->fval : s->fval;
}
}
while (status == GSL_CONTINUE && iter < 10000);
gsl_vector_free(x);
gsl_vector_free(ss);
gsl_multimin_fminimizer_free (s);
return result;
}
#else /* HAVE_GSL */
qreal calculateMaxWeight(const QPainterPath &selectionPath,
qreal exponent,
bool searchForMax)
{
QPointF center = centerFromPath(selectionPath);
return searchForMax ?
getDisnormedGradientValue(center, selectionPath, exponent) : 0.0;
}
#endif /* HAVE_GSL */
}
QPainterPath simplifyPath(const QPainterPath &path,
qreal sizePortion,
qreal minLinearSize,
int minNumSamples)
{
QPainterPath finalPath;
QList<QPolygonF> polygons = path.toSubpathPolygons();
foreach (const QPolygonF poly, polygons) {
QPainterPath p;
p.addPolygon(poly);
const qreal length = p.length();
const qreal lengthStep =
KritaUtils::maxDimensionPortion(poly.boundingRect(),
sizePortion, minLinearSize);
int numSamples = qMax(qCeil(length / lengthStep), minNumSamples);
if (numSamples > poly.size()) {
finalPath.addPolygon(poly);
finalPath.closeSubpath();
continue;
}
const qreal portionStep = 1.0 / numSamples;
QPolygonF newPoly;
for (qreal t = 0.0; t < 1.0; t += portionStep) {
newPoly << p.pointAtPercent(t);
}
finalPath.addPolygon(newPoly);
finalPath.closeSubpath();
}
return finalPath;
}
KisPolygonalGradientShapeStrategy::KisPolygonalGradientShapeStrategy(const QPainterPath &selectionPath,
qreal exponent)
: m_exponent(exponent)
{
m_selectionPath = simplifyPath(selectionPath, 0.01, 3.0, 100);
m_maxWeight = Private::calculateMaxWeight(m_selectionPath, m_exponent, true);
m_minWeight = Private::calculateMaxWeight(m_selectionPath, m_exponent, false);
m_scaleCoeff = 1.0 / (m_maxWeight - m_minWeight);
}
KisPolygonalGradientShapeStrategy::~KisPolygonalGradientShapeStrategy()
{
}
double KisPolygonalGradientShapeStrategy::valueAt(double x, double y) const
{
QPointF pt(x, y);
qreal value = 0.0;
if (m_selectionPath.contains(pt)) {
value = Private::getDisnormedGradientValue(pt, m_selectionPath, m_exponent);
value = (value - m_minWeight) * m_scaleCoeff;
}
return value;
}
QPointF KisPolygonalGradientShapeStrategy::testingCalculatePathCenter(int numSamples, const QPainterPath &path, qreal exponent, bool searchForMax)
{
QPointF result;
qreal extremumValue = Private::initialExtremumValue(searchForMax);
bool success = Private::findBestStartingPoint(numSamples, path,
exponent, searchForMax,
extremumValue,
&result);
if (!success) {
- qDebug() << "WARNING: Couldn't calculate findBestStartingPoint for:";
- qDebug() << ppVar(numSamples);
- qDebug() << ppVar(exponent);
- qDebug() << ppVar(searchForMax);
- qDebug() << ppVar(path);
+ dbgKrita << "WARNING: Couldn't calculate findBestStartingPoint for:";
+ dbgKrita << ppVar(numSamples);
+ dbgKrita << ppVar(exponent);
+ dbgKrita << ppVar(searchForMax);
+ dbgKrita << ppVar(path);
}
return result;
}
diff --git a/krita/image/kis_recalculate_transform_mask_job.cpp b/krita/image/kis_recalculate_transform_mask_job.cpp
index 19faae4117e..fde7b060063 100644
--- a/krita/image/kis_recalculate_transform_mask_job.cpp
+++ b/krita/image/kis_recalculate_transform_mask_job.cpp
@@ -1,61 +1,61 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_recalculate_transform_mask_job.h"
#include "kis_transform_mask.h"
#include "kis_debug.h"
#include "kis_layer.h"
#include "kis_image.h"
KisRecalculateTransformMaskJob::KisRecalculateTransformMaskJob(KisTransformMaskSP mask)
: m_mask(mask)
{
}
bool KisRecalculateTransformMaskJob::overrides(const KisSpontaneousJob *_otherJob)
{
const KisRecalculateTransformMaskJob *otherJob =
dynamic_cast<const KisRecalculateTransformMaskJob*>(_otherJob);
return otherJob && otherJob->m_mask == m_mask;
}
void KisRecalculateTransformMaskJob::run()
{
/**
* The mask might have been deleted from the layers stack. In
* such a case, don't try do update it.
*/
if (!m_mask->parent()) return;
m_mask->recaclulateStaticImage();
KisLayerSP layer = dynamic_cast<KisLayer*>(m_mask->parent().data());
if (!layer) {
- qWarning() << "WARNING: KisRecalculateTransformMaskJob::run() Mask has no parent layer! Skipping projection update!";
+ warnKrita << "WARNING: KisRecalculateTransformMaskJob::run() Mask has no parent layer! Skipping projection update!";
return;
}
KisImageSP image = layer->image();
Q_ASSERT(image);
image->requestProjectionUpdateNoFilthy(layer, layer->extent(), image->bounds());
}
diff --git a/krita/image/kis_shared_ptr.h b/krita/image/kis_shared_ptr.h
index d13c3166721..4b35c488af6 100644
--- a/krita/image/kis_shared_ptr.h
+++ b/krita/image/kis_shared_ptr.h
@@ -1,508 +1,508 @@
/*
* Copyright (c) 2005 Frerich Raabe <raabe@kde.org>
* Copyright (c) 2006,2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_SHAREDPTR_H
#define KIS_SHAREDPTR_H
#include <QtGlobal>
#include <kis_debug.h>
#include "kis_memory_leak_tracker.h"
template<class T>
class KisWeakSharedPtr;
/**
* KisSharedPtr is a shared pointer similar to KSharedPtr and
* boost::shared_ptr. The difference with KSharedPtr is that our
* constructor is not explicit.
*
* A shared pointer is a wrapper around a real pointer. The shared
* pointer keeps a reference count, and when the reference count drops
* to 0 the contained pointer is deleted. You can use the shared
* pointer just as you would use a real pointer.
*
* See also also item 28 and 29 of More Effective C++ and
* http://bugs.kde.org/show_bug.cgi?id=52261 as well as
* http://www.boost.org/libs/smart_ptr/shared_ptr.htm.
*
* Advantage of KisSharedPtr over boost pointer or QSharedPointer?
*
* The difference with boost share pointer is that in
* boost::shared_ptr, the counter is kept inside the smart pointer,
* meaning that you should never never remove the pointer from its
* smart pointer object, because if you do that, and somewhere in the
* code, the pointer is put back in a smart pointer, then you have two
* counters, and when one reach zero, then the object gets deleted
* while some other code thinks the pointer is still valid.
*
* Disadvantage of KisSharedPtr compared to boost pointer?
*
* KisSharedPtr requires the class to inherits KisShared.
*
* Difference with QSharedDataPointer
*
* QSharedDataPointer and KisSharedPtr are very similar, but
* QSharedDataPointer has an explicit constructor which makes it more
* painful to use in some constructions. And QSharedDataPointer
* doesn't offer a weak pointer.
*/
template<class T>
class KisSharedPtr
{
friend class KisWeakSharedPtr<T>;
public:
/**
* Creates a null pointer.
*/
inline KisSharedPtr()
: d(0) { }
/**
* Creates a new pointer.
* @param p the pointer
*/
inline KisSharedPtr(T* p)
: d(p) {
ref();
}
inline KisSharedPtr(const KisWeakSharedPtr<T>& o);
// Free the pointer and set it to new value
void attach(T* p);
// Free the pointer
void clear();
/**
* Copies a pointer.
* @param o the pointer to copy
*/
inline KisSharedPtr<T>(const KisSharedPtr<T>& o)
: d(o.d) {
ref();
}
/**
* Dereferences the object that this pointer points to. If it was
* the last reference, the object will be deleted.
*/
inline ~KisSharedPtr() {
deref();
}
inline KisSharedPtr<T>& operator= (const KisSharedPtr& o) {
attach(o.d);
return *this;
}
inline bool operator== (const T* p) const {
return (d == p);
}
inline bool operator!= (const T* p) const {
return (d != p);
}
inline bool operator== (const KisSharedPtr& o) const {
return (d == o.d);
}
inline bool operator!= (const KisSharedPtr& o) const {
return (d != o.d);
}
inline KisSharedPtr<T>& operator= (T* p) {
attach(p);
return *this;
}
inline operator const T*() const {
return d;
}
template< class T2> inline operator KisSharedPtr<T2>() const {
return KisSharedPtr<T2>(d);
}
/**
* @return the contained pointer. If you delete the contained
* pointer, you will make KisSharedPtr very unhappy. It is
* perfectly save to put the contained pointer in another
* KisSharedPtr, though.
*/
inline T* data() {
return d;
}
/**
* @return the pointer
*/
inline const T* data() const {
return d;
}
/**
* @return a const pointer to the shared object.
*/
inline const T* constData() const {
return d;
}
inline const T& operator*() const {
Q_ASSERT(d);
return *d;
}
inline T& operator*() {
Q_ASSERT(d);
return *d;
}
inline const T* operator->() const {
Q_ASSERT(d);
return d;
}
inline T* operator->() {
Q_ASSERT(d);
return d;
}
/**
* @return true if the pointer is null
*/
inline bool isNull() const {
return (d == 0);
}
private:
inline static void ref(const KisSharedPtr<T>* sp, T* t)
{
#ifndef HAVE_MEMORY_LEAK_TRACKER
Q_UNUSED(sp);
#else
KisMemoryLeakTracker::instance()->reference(t, sp);
#endif
if (t) {
t->ref();
}
}
inline void ref() const
{
ref(this, d);
}
inline static bool deref(const KisSharedPtr<T>* sp, T* t)
{
#ifndef HAVE_MEMORY_LEAK_TRACKER
Q_UNUSED(sp);
#else
KisMemoryLeakTracker::instance()->dereference(t, sp);
#endif
if (t && !t->deref()) {
delete t;
return false;
}
return true;
}
inline void deref() const
{
bool v = deref(this, d);
#ifndef NDEBUG
if (!v) {
d = 0;
}
#else
Q_UNUSED(v);
#endif
}
private:
mutable T* d;
};
/**
* A weak shared ptr is an ordinary shared ptr, with two differences:
* it doesn't delete the contained pointer if the refcount drops to
* zero and it doesn't prevent the contained pointer from being
* deleted if the last strong shared pointer goes out of scope.
*/
template<class T>
class KisWeakSharedPtr
{
friend class KisSharedPtr<T>;
public:
/**
* Creates a null pointer.
*/
inline KisWeakSharedPtr()
: d(0), weakReference(0) { }
/**
* Creates a new pointer.
* @param p the pointer
*/
inline KisWeakSharedPtr(T* p) {
load(p);
}
inline KisWeakSharedPtr<T>(const KisSharedPtr<T>& o) {
load(o.d);
}
/**
* Copies a pointer.
* @param o the pointer to copy
*/
inline KisWeakSharedPtr<T>(const KisWeakSharedPtr<T>& o) {
if (o.isConsistent()) {
load(o.d);
}
else {
d = 0;
weakReference = 0;
}
}
inline ~KisWeakSharedPtr() {
detach();
}
inline KisWeakSharedPtr<T>& operator= (const KisWeakSharedPtr& o) {
attach(o);
return *this;
}
inline bool operator== (const T* p) const {
return (d == p);
}
inline bool operator!= (const T* p) const {
return (d != p);
}
inline bool operator== (const KisWeakSharedPtr& o) const {
return (d == o.d);
}
inline bool operator!= (const KisWeakSharedPtr& o) const {
return (d != o.d);
}
inline KisWeakSharedPtr<T>& operator= (T* p) {
attach(p);
return *this;
}
template< class T2> inline operator KisWeakSharedPtr<T2>() const {
return KisWeakSharedPtr<T2>(d);
}
/**
* Note that if you use this function, the pointer might be destroyed
* if KisSharedPtr pointing to this pointer are deleted, resulting in
* a segmentation fault. Use with care.
* @return a const pointer to the shared object.
*/
inline T* data() {
if (!isConsistent()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @see data()
*/
inline const T* data() const {
if (!isConsistent()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @see data()
*/
inline const T* constData() const {
if (!isConsistent()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @see data()
*/
inline operator const T*() const {
/**
* This operator is used in boolean expressions where we check
* for pointer consistency, so return 0 instead of asserting.
*/
return isConsistent() ? d : 0;
}
inline const T& operator*() const {
if (!isValid()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return *d;
}
inline T& operator*() {
if (!isValid()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return *d;
}
inline const T* operator->() const {
if (!isValid()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
inline T* operator->() {
if (!isValid()) {
- warnKrita << kBacktrace();
+ warnKrita << kisBacktrace();
Q_ASSERT_X(0, "KisWeakSharedPtr", "Weak pointer is not valid!");
}
return d;
}
/**
* @return true if the pointer is null
*/
inline bool isNull() const {
return (d == 0);
}
/**
* @return true if the weak pointer points to a valid pointer
* and false if the data has been deleted or is null
*/
inline bool isValid() const {
Q_ASSERT(!d || weakReference);
return d && weakReference && isOdd((int)*weakReference);
}
private:
static const qint32 WEAK_REF = 2;
static inline bool isOdd(const qint32 &x) {
return x & 0x01;
}
inline bool isConsistent() const {
Q_ASSERT(!d || weakReference);
return !d || (weakReference && isOdd((int)*weakReference));
}
void load(T* newValue) {
d = newValue;
if (d) {
weakReference = d->sharedWeakReference();
weakReference->fetchAndAddOrdered(WEAK_REF);
}
else {
weakReference = 0;
}
}
// see note in kis_shared.cc
inline void attach(T* newValue) {
detach();
load(newValue);
}
inline void attach(const KisWeakSharedPtr& o) {
detach();
if (o.isConsistent()) {
load(o.d);
}
else {
d = 0;
weakReference = 0;
}
}
// see note in kis_shared.cc
void detach() {
d = 0;
if (weakReference &&
weakReference->fetchAndAddOrdered(-WEAK_REF) <= WEAK_REF) {
// sanity check:
Q_ASSERT((int)*weakReference == 0);
delete weakReference;
weakReference = 0;
}
}
mutable T* d;
QAtomicInt *weakReference;
};
template <class T>
Q_INLINE_TEMPLATE KisSharedPtr<T>::KisSharedPtr(const KisWeakSharedPtr<T>& o)
: d(o.d)
{
if (o.isValid()) {
ref();
/**
* Thread safety:
* Is the object we have just referenced still valid?
*/
Q_ASSERT(o.isConsistent());
}
else {
d = 0;
}
}
template <class T>
Q_INLINE_TEMPLATE void KisSharedPtr<T>::attach(T* p)
{
if (d != p) {
ref(this, p);
T* old = d;
d = p;
deref(this, old);
}
}
template <class T>
Q_INLINE_TEMPLATE void KisSharedPtr<T>::clear()
{
attach((T*)0);
}
#endif
diff --git a/krita/image/kis_simple_update_queue.cpp b/krita/image/kis_simple_update_queue.cpp
index 041cede4139..e117fac9048 100644
--- a/krita/image/kis_simple_update_queue.cpp
+++ b/krita/image/kis_simple_update_queue.cpp
@@ -1,355 +1,355 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_simple_update_queue.h"
#include <QMutexLocker>
#include "kis_image_config.h"
#include "kis_full_refresh_walker.h"
#include "kis_spontaneous_job.h"
//#define ENABLE_DEBUG_JOIN
//#define ENABLE_ACCUMULATOR
#ifdef ENABLE_DEBUG_JOIN
#define DEBUG_JOIN(baseRect, newRect, alpha) \
- qDebug() << "Two rects were joined:\t" \
+ dbgKrita << "Two rects were joined:\t" \
<< (baseRect) << "+" << (newRect) << "->" \
<< ((baseRect) | (newRect)) << "(" << alpha << ")"
#else
#define DEBUG_JOIN(baseRect, newRect, alpha)
#endif /* ENABLE_DEBUG_JOIN */
#ifdef ENABLE_ACCUMULATOR
#define DECLARE_ACCUMULATOR() static qreal _baseAmount=0, _newAmount=0
#define ACCUMULATOR_ADD(baseAmount, newAmount) \
do {_baseAmount += baseAmount; _newAmount += newAmount;} while (0)
#define ACCUMULATOR_DEBUG() \
- qDebug() << "Accumulated alpha:" << _newAmount / _baseAmount
+ dbgKrita << "Accumulated alpha:" << _newAmount / _baseAmount
#else
#define DECLARE_ACCUMULATOR()
#define ACCUMULATOR_ADD(baseAmount, newAmount)
#define ACCUMULATOR_DEBUG()
#endif /* ENABLE_ACCUMULATOR */
KisSimpleUpdateQueue::KisSimpleUpdateQueue()
{
updateSettings();
}
KisSimpleUpdateQueue::~KisSimpleUpdateQueue()
{
QMutexLocker locker(&m_lock);
while (!m_spontaneousJobsList.isEmpty()) {
delete m_spontaneousJobsList.takeLast();
}
}
void KisSimpleUpdateQueue::updateSettings()
{
KisImageConfig config;
m_patchWidth = config.updatePatchWidth();
m_patchHeight = config.updatePatchHeight();
m_maxCollectAlpha = config.maxCollectAlpha();
m_maxMergeAlpha = config.maxMergeAlpha();
m_maxMergeCollectAlpha = config.maxMergeCollectAlpha();
}
void KisSimpleUpdateQueue::processQueue(KisUpdaterContext &updaterContext)
{
updaterContext.lock();
while(updaterContext.hasSpareThread() &&
processOneJob(updaterContext));
updaterContext.unlock();
}
bool KisSimpleUpdateQueue::processOneJob(KisUpdaterContext &updaterContext)
{
QMutexLocker locker(&m_lock);
KisBaseRectsWalkerSP item;
KisMutableWalkersListIterator iter(m_updatesList);
bool jobAdded = false;
while(iter.hasNext()) {
item = iter.next();
if(!item->checksumValid())
item->recalculate(item->requestedRect());
if(updaterContext.isJobAllowed(item)) {
updaterContext.addMergeJob(item);
iter.remove();
jobAdded = true;
break;
}
}
if (jobAdded) return true;
if (!m_spontaneousJobsList.isEmpty()) {
/**
* WARNING: Please note that this still doesn't guarantee that
* the spontaneous jobs are exclusive, since updates and/or
* strokes can be added after them. The only thing it
* guarantees that two spontaneous jobs will not be executed
* in parallel.
*
* Right now it works as it is. Probably will need to be fixed
* in the future.
*/
qint32 numMergeJobs;
qint32 numStrokeJobs;
updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs);
if (!numMergeJobs && !numStrokeJobs) {
KisSpontaneousJob *job = m_spontaneousJobsList.takeFirst();
updaterContext.addSpontaneousJob(job);
jobAdded = true;
}
}
return jobAdded;
}
void KisSimpleUpdateQueue::addUpdateJob(KisNodeSP node, const QRect& rc, const QRect& cropRect)
{
addJob(node, rc, cropRect, KisBaseRectsWalker::UPDATE);
}
void KisSimpleUpdateQueue::addUpdateNoFilthyJob(KisNodeSP node, const QRect& rc, const QRect& cropRect)
{
addJob(node, rc, cropRect, KisBaseRectsWalker::UPDATE_NO_FILTHY);
}
void KisSimpleUpdateQueue::addFullRefreshJob(KisNodeSP node, const QRect& rc, const QRect& cropRect)
{
addJob(node, rc, cropRect, KisBaseRectsWalker::FULL_REFRESH);
}
void KisSimpleUpdateQueue::addJob(KisNodeSP node, const QRect& rc,
const QRect& cropRect,
KisBaseRectsWalker::UpdateType type)
{
if(trySplitJob(node, rc, cropRect, type)) return;
if(tryMergeJob(node, rc, cropRect, type)) return;
KisBaseRectsWalkerSP walker;
if (type == KisBaseRectsWalker::UPDATE) {
walker = new KisMergeWalker(cropRect, KisMergeWalker::DEFAULT);
}
else if (type == KisBaseRectsWalker::FULL_REFRESH) {
walker = new KisFullRefreshWalker(cropRect);
}
else if (type == KisBaseRectsWalker::UPDATE_NO_FILTHY) {
walker = new KisMergeWalker(cropRect, KisMergeWalker::NO_FILTHY);
}
- /* else if(type == KisBaseRectsWalker::UNSUPPORTED) qFatal(); */
+ /* else if(type == KisBaseRectsWalker::UNSUPPORTED) fatalKrita; */
walker->collectRects(node, rc);
m_lock.lock();
m_updatesList.append(walker);
m_lock.unlock();
}
void KisSimpleUpdateQueue::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
QMutexLocker locker(&m_lock);
KisSpontaneousJob *item;
KisMutableSpontaneousJobsListIterator iter(m_spontaneousJobsList);
iter.toBack();
while(iter.hasPrevious()) {
item = iter.previous();
if (spontaneousJob->overrides(item)) {
iter.remove();
delete item;
}
}
m_spontaneousJobsList.append(spontaneousJob);
}
bool KisSimpleUpdateQueue::isEmpty() const
{
QMutexLocker locker(&m_lock);
return m_updatesList.isEmpty() && m_spontaneousJobsList.isEmpty();
}
qint32 KisSimpleUpdateQueue::sizeMetric() const
{
QMutexLocker locker(&m_lock);
return m_updatesList.size() + m_spontaneousJobsList.size();
}
bool KisSimpleUpdateQueue::trySplitJob(KisNodeSP node, const QRect& rc,
const QRect& cropRect,
KisBaseRectsWalker::UpdateType type)
{
if(rc.width() <= m_patchWidth || rc.height() <= m_patchHeight)
return false;
// a bit of recursive splitting...
qint32 firstCol = rc.x() / m_patchWidth;
qint32 firstRow = rc.y() / m_patchHeight;
qint32 lastCol = (rc.x() + rc.width()) / m_patchWidth;
qint32 lastRow = (rc.y() + rc.height()) / m_patchHeight;
for(qint32 i = firstRow; i <= lastRow; i++) {
for(qint32 j = firstCol; j <= lastCol; j++) {
QRect maxPatchRect(j * m_patchWidth, i * m_patchHeight,
m_patchWidth, m_patchHeight);
QRect patchRect = rc & maxPatchRect;
addJob(node, patchRect, cropRect, type);
}
}
return true;
}
bool KisSimpleUpdateQueue::tryMergeJob(KisNodeSP node, const QRect& rc,
const QRect& cropRect,
KisBaseRectsWalker::UpdateType type)
{
QMutexLocker locker(&m_lock);
QRect baseRect = rc;
KisBaseRectsWalkerSP goodCandidate;
KisBaseRectsWalkerSP item;
KisWalkersListIterator iter(m_updatesList);
/**
* We add new jobs to the tail of the list,
* so it's more probable to find a good candidate here.
*/
iter.toBack();
while(iter.hasPrevious()) {
item = iter.previous();
if(item->startNode() != node) continue;
if(item->type() != type) continue;
if(item->cropRect() != cropRect) continue;
if(joinRects(baseRect, item->requestedRect(), m_maxMergeAlpha)) {
goodCandidate = item;
break;
}
}
if(goodCandidate)
collectJobs(goodCandidate, baseRect, node, m_maxMergeCollectAlpha);
return (bool)goodCandidate;
}
void KisSimpleUpdateQueue::optimize()
{
QMutexLocker locker(&m_lock);
if(m_updatesList.size() <= 1) return;
KisBaseRectsWalkerSP baseWalker = m_updatesList.first();
QRect baseRect = baseWalker->requestedRect();
KisNodeSP baseNode = baseWalker->startNode();
collectJobs(baseWalker, baseRect, baseNode, m_maxCollectAlpha);
}
void KisSimpleUpdateQueue::collectJobs(KisBaseRectsWalkerSP &baseWalker,
QRect baseRect,
const KisNodeSP &baseNode,
const qreal maxAlpha)
{
KisBaseRectsWalkerSP item;
KisMutableWalkersListIterator iter(m_updatesList);
while(iter.hasNext()) {
item = iter.next();
if(item == baseWalker) continue;
if(item->type() != baseWalker->type()) continue;
if(item->startNode() != baseNode) continue;
if(item->cropRect() != baseWalker->cropRect()) continue;
if(joinRects(baseRect, item->requestedRect(), maxAlpha)) {
iter.remove();
}
}
if(baseWalker->requestedRect() != baseRect) {
baseWalker->collectRects(baseNode, baseRect);
}
}
bool KisSimpleUpdateQueue::joinRects(QRect& baseRect,
const QRect& newRect, qreal maxAlpha)
{
QRect unitedRect = baseRect | newRect;
if(unitedRect.width() > m_patchWidth || unitedRect.height() > m_patchHeight)
return false;
bool result = false;
qint64 baseWork = baseRect.width() * baseRect.height() +
newRect.width() * newRect.height();
qint64 newWork = unitedRect.width() * unitedRect.height();
qreal alpha = qreal(newWork) / baseWork;
if(alpha < maxAlpha) {
DEBUG_JOIN(baseRect, newRect, alpha);
DECLARE_ACCUMULATOR();
ACCUMULATOR_ADD(baseWork, newWork);
ACCUMULATOR_DEBUG();
baseRect = unitedRect;
result = true;
}
return result;
}
KisWalkersList& KisTestableSimpleUpdateQueue::getWalkersList()
{
return m_updatesList;
}
KisSpontaneousJobsList& KisTestableSimpleUpdateQueue::getSpontaneousJobsList()
{
return m_spontaneousJobsList;
}
diff --git a/krita/image/kis_stroke_strategy_undo_command_based.cpp b/krita/image/kis_stroke_strategy_undo_command_based.cpp
index e8335654eb6..859e9117ecc 100644
--- a/krita/image/kis_stroke_strategy_undo_command_based.cpp
+++ b/krita/image/kis_stroke_strategy_undo_command_based.cpp
@@ -1,144 +1,144 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_stroke_strategy_undo_command_based.h"
#include <QMutexLocker>
#include "kis_post_execution_undo_adapter.h"
#include "commands_new/kis_saved_commands.h"
KisStrokeStrategyUndoCommandBased::
KisStrokeStrategyUndoCommandBased(const KUndo2MagicString &name,
bool undo,
KisPostExecutionUndoAdapter *undoAdapter,
KUndo2CommandSP initCommand,
KUndo2CommandSP finishCommand)
: KisSimpleStrokeStrategy("STROKE_UNDO_COMMAND_BASED", name),
m_undo(undo),
m_initCommand(initCommand),
m_finishCommand(finishCommand),
m_undoAdapter(undoAdapter),
m_macroCommand(0)
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
}
void KisStrokeStrategyUndoCommandBased::setUsedWhileUndoRedo(bool value)
{
setClearsRedoOnStart(!value);
}
void KisStrokeStrategyUndoCommandBased::executeCommand(KUndo2CommandSP command, bool undo)
{
if(!command) return;
if(undo) {
command->undo();
} else {
command->redo();
}
}
void KisStrokeStrategyUndoCommandBased::initStrokeCallback()
{
if(m_undoAdapter) {
m_macroCommand = m_undoAdapter->createMacro(name());
}
executeCommand(m_initCommand, m_undo);
notifyCommandDone(m_initCommand,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
void KisStrokeStrategyUndoCommandBased::finishStrokeCallback()
{
executeCommand(m_finishCommand, m_undo);
notifyCommandDone(m_finishCommand,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
QMutexLocker locker(&m_mutex);
if(m_macroCommand) {
Q_ASSERT(m_undoAdapter);
postProcessToplevelCommand(m_macroCommand);
m_undoAdapter->addMacro(m_macroCommand);
m_macroCommand = 0;
}
}
void KisStrokeStrategyUndoCommandBased::cancelStrokeCallback()
{
QMutexLocker locker(&m_mutex);
if(m_macroCommand) {
m_macroCommand->performCancel(cancelStrokeId(), m_undo);
delete m_macroCommand;
m_macroCommand = 0;
}
}
void KisStrokeStrategyUndoCommandBased::doStrokeCallback(KisStrokeJobData *data)
{
Data *d = dynamic_cast<Data*>(data);
executeCommand(d->command, d->undo);
notifyCommandDone(d->command, d->sequentiality(), d->exclusivity());
}
void KisStrokeStrategyUndoCommandBased::runAndSaveCommand(KUndo2CommandSP command,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
if (!command) return;
command->redo();
notifyCommandDone(command, sequentiality, exclusivity);
}
void KisStrokeStrategyUndoCommandBased::notifyCommandDone(KUndo2CommandSP command,
KisStrokeJobData::Sequentiality sequentiality,
KisStrokeJobData::Exclusivity exclusivity)
{
if(!command) return;
QMutexLocker locker(&m_mutex);
if(m_macroCommand) {
m_macroCommand->addCommand(command, sequentiality, exclusivity);
}
}
void KisStrokeStrategyUndoCommandBased::setCommandExtraData(KUndo2CommandExtraData *data)
{
if (m_undoAdapter && m_macroCommand) {
- qWarning() << "WARNING: KisStrokeStrategyUndoCommandBased::setCommandExtraData():"
+ warnKrita << "WARNING: KisStrokeStrategyUndoCommandBased::setCommandExtraData():"
<< "the extra data is set while the stroke has already been started!"
<< "The result is undefined, continued actions may not work!";
}
m_commandExtraData.reset(data);
}
void KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(KUndo2Command *command)
{
if (m_commandExtraData) {
command->setExtraData(m_commandExtraData.take());
}
}
diff --git a/krita/image/kis_transaction_data.cpp b/krita/image/kis_transaction_data.cpp
index 03be0602fa0..e360840680f 100644
--- a/krita/image/kis_transaction_data.cpp
+++ b/krita/image/kis_transaction_data.cpp
@@ -1,236 +1,236 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transaction_data.h"
#include "kis_pixel_selection.h"
#include "kis_paint_device.h"
#include "kis_datamanager.h"
#include <config-tiles.h> // For the next define
//#define DEBUG_TRANSACTIONS
#ifdef DEBUG_TRANSACTIONS
- #define DEBUG_ACTION(action) qDebug() << action << "for" << m_d->device->dataManager()
+ #define DEBUG_ACTION(action) dbgKrita << action << "for" << m_d->device->dataManager()
#else
#define DEBUG_ACTION(action)
#endif
class Q_DECL_HIDDEN KisTransactionData::Private
{
public:
KisPaintDeviceSP device;
KisMementoSP memento;
bool firstRedo;
bool transactionFinished;
QPoint oldOffset;
QPoint newOffset;
bool savedOutlineCacheValid;
QPainterPath savedOutlineCache;
KUndo2Command *flattenUndoCommand;
bool resetSelectionOutlineCache;
};
KisTransactionData::KisTransactionData(const KUndo2MagicString& name, KisPaintDeviceSP device, bool resetSelectionOutlineCache, KUndo2Command* parent)
: KUndo2Command(name, parent)
, m_d(new Private())
{
m_d->resetSelectionOutlineCache = resetSelectionOutlineCache;
setTimedID(-1);
init(device);
saveSelectionOutlineCache();
}
void KisTransactionData::init(KisPaintDeviceSP device)
{
m_d->device = device;
DEBUG_ACTION("Transaction started");
m_d->memento = device->dataManager()->getMemento();
m_d->oldOffset = QPoint(device->x(), device->y());
m_d->firstRedo = true;
m_d->transactionFinished = false;
m_d->flattenUndoCommand = 0;
}
KisTransactionData::~KisTransactionData()
{
Q_ASSERT(m_d->memento);
m_d->device->dataManager()->purgeHistory(m_d->memento);
delete m_d;
}
void KisTransactionData::endTransaction()
{
if(!m_d->transactionFinished) {
DEBUG_ACTION("Transaction ended");
m_d->transactionFinished = true;
m_d->device->dataManager()->commit();
m_d->newOffset = QPoint(m_d->device->x(), m_d->device->y());
}
}
void KisTransactionData::startUpdates()
{
QRect rc;
QRect mementoExtent = m_d->memento->extent();
if (m_d->newOffset == m_d->oldOffset) {
rc = mementoExtent.translated(m_d->device->x(), m_d->device->y());
} else {
QRect totalExtent =
m_d->device->dataManager()->extent() | mementoExtent;
rc = totalExtent.translated(m_d->oldOffset) |
totalExtent.translated(m_d->newOffset);
}
m_d->device->setDirty(rc);
}
void KisTransactionData::possiblyNotifySelectionChanged()
{
KisPixelSelectionSP pixelSelection =
dynamic_cast<KisPixelSelection*>(m_d->device.data());
KisSelectionSP selection;
if (pixelSelection && (selection = pixelSelection->parentSelection())) {
selection->notifySelectionChanged();
}
}
void KisTransactionData::possiblyResetOutlineCache()
{
KisPixelSelectionSP pixelSelection;
if (m_d->resetSelectionOutlineCache &&
(pixelSelection =
dynamic_cast<KisPixelSelection*>(m_d->device.data()))) {
pixelSelection->invalidateOutlineCache();
}
}
void KisTransactionData::redo()
{
//KUndo2QStack calls redo(), so the first call needs to be blocked
if (m_d->firstRedo) {
m_d->firstRedo = false;
possiblyResetOutlineCache();
possiblyNotifySelectionChanged();
return;
}
restoreSelectionOutlineCache(false);
DEBUG_ACTION("Redo()");
Q_ASSERT(m_d->memento);
m_d->device->dataManager()->rollforward(m_d->memento);
if (m_d->newOffset != m_d->oldOffset) {
m_d->device->move(m_d->newOffset);
}
startUpdates();
possiblyNotifySelectionChanged();
}
void KisTransactionData::undo()
{
DEBUG_ACTION("Undo()");
Q_ASSERT(m_d->memento);
m_d->device->dataManager()->rollback(m_d->memento);
if (m_d->newOffset != m_d->oldOffset) {
m_d->device->move(m_d->oldOffset);
}
restoreSelectionOutlineCache(true);
startUpdates();
possiblyNotifySelectionChanged();
}
void KisTransactionData::saveSelectionOutlineCache()
{
m_d->savedOutlineCacheValid = false;
KisPixelSelectionSP pixelSelection =
dynamic_cast<KisPixelSelection*>(m_d->device.data());
if (pixelSelection) {
m_d->savedOutlineCacheValid = pixelSelection->outlineCacheValid();
if (m_d->savedOutlineCacheValid) {
m_d->savedOutlineCache = pixelSelection->outlineCache();
possiblyResetOutlineCache();
}
KisSelectionSP selection = pixelSelection->parentSelection();
if (selection) {
m_d->flattenUndoCommand = selection->flatten();
if (m_d->flattenUndoCommand) {
m_d->flattenUndoCommand->redo();
}
}
}
}
void KisTransactionData::restoreSelectionOutlineCache(bool undo)
{
KisPixelSelectionSP pixelSelection =
dynamic_cast<KisPixelSelection*>(m_d->device.data());
if (pixelSelection) {
bool savedOutlineCacheValid;
QPainterPath savedOutlineCache;
savedOutlineCacheValid = pixelSelection->outlineCacheValid();
if (savedOutlineCacheValid) {
savedOutlineCache = pixelSelection->outlineCache();
}
if (m_d->savedOutlineCacheValid) {
pixelSelection->setOutlineCache(m_d->savedOutlineCache);
} else {
pixelSelection->invalidateOutlineCache();
}
m_d->savedOutlineCacheValid = savedOutlineCacheValid;
if (m_d->savedOutlineCacheValid) {
m_d->savedOutlineCache = savedOutlineCache;
}
if (m_d->flattenUndoCommand) {
if (undo) {
m_d->flattenUndoCommand->undo();
} else {
m_d->flattenUndoCommand->redo();
}
}
}
}
diff --git a/krita/image/kis_transform_mask.cpp b/krita/image/kis_transform_mask.cpp
index c4187b1de6f..0398a911764 100644
--- a/krita/image/kis_transform_mask.cpp
+++ b/krita/image/kis_transform_mask.cpp
@@ -1,395 +1,395 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <KoIcon.h>
#include <KoCompositeOpRegistry.h>
#include "kis_layer.h"
#include "kis_transform_mask.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "kis_node.h"
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_node_progress_proxy.h"
#include "kis_transaction.h"
#include "kis_painter.h"
#include "kis_busy_progress_indicator.h"
#include "kis_perspectivetransform_worker.h"
#include "kis_transform_mask_params_interface.h"
#include "kis_recalculate_transform_mask_job.h"
#include "kis_signal_compressor.h"
#include "kis_algebra_2d.h"
#include "kis_safe_transform.h"
#include "kis_image_config.h"
//#define DEBUG_RENDERING
//#define DUMP_RECT QRect(0,0,512,512)
#define UPDATE_DELAY 3000 /*ms */
struct Q_DECL_HIDDEN KisTransformMask::Private
{
Private()
: worker(0, QTransform(), 0),
staticCacheValid(false),
recalculatingStaticImage(false),
updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::POSTPONE),
offBoundsReadArea(0.5)
{
}
Private(const Private &rhs)
: worker(rhs.worker),
params(rhs.params),
staticCacheValid(false),
recalculatingStaticImage(rhs.recalculatingStaticImage),
updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::POSTPONE)
{
}
KisPerspectiveTransformWorker worker;
KisTransformMaskParamsInterfaceSP params;
bool staticCacheValid;
bool recalculatingStaticImage;
KisPaintDeviceSP staticCacheDevice;
KisSignalCompressor updateSignalCompressor;
qreal offBoundsReadArea;
};
KisTransformMask::KisTransformMask()
: KisEffectMask(),
m_d(new Private)
{
setTransformParams(
KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams()));
connect(this, SIGNAL(initiateDelayedStaticUpdate()), &m_d->updateSignalCompressor, SLOT(start()));
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
KisImageConfig cfg;
m_d->offBoundsReadArea = cfg.transformMaskOffBoundsReadArea();
}
KisTransformMask::~KisTransformMask()
{
}
KisTransformMask::KisTransformMask(const KisTransformMask& rhs)
: KisEffectMask(rhs),
m_d(new Private(*rhs.m_d))
{
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
}
KisPaintDeviceSP KisTransformMask::paintDevice() const
{
return 0;
}
QIcon KisTransformMask::icon() const
{
return themedIcon("edit-cut");
}
void KisTransformMask::setTransformParams(KisTransformMaskParamsInterfaceSP params)
{
KIS_ASSERT_RECOVER(params) {
params = KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams());
}
QTransform affineTransform;
if (params->isAffine()) {
affineTransform = params->finalAffineTransform();
}
m_d->worker.setForwardTransform(affineTransform);
m_d->params = params;
m_d->updateSignalCompressor.stop();
}
KisTransformMaskParamsInterfaceSP KisTransformMask::transformParams() const
{
return m_d->params;
}
void KisTransformMask::slotDelayedStaticUpdate()
{
/**
* The mask might have been deleted from the layers stack in the
* meanwhile. Just ignore the updates in the case.
*/
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
if (!parentLayer) return;
KisImageSP image = parentLayer->image();
if (image) {
image->addSpontaneousJob(new KisRecalculateTransformMaskJob(this));
}
}
KisPaintDeviceSP KisTransformMask::buildPreviewDevice()
{
/**
* Note: this function must be called from within the scheduler's
* context. We are accessing parent's updateProjection(), which
* is not entirely safe.
*/
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
KIS_ASSERT_RECOVER(parentLayer) { return new KisPaintDevice(colorSpace()); }
KisPaintDeviceSP device =
new KisPaintDevice(parentLayer->original()->colorSpace());
QRect requestedRect = parentLayer->original()->exactBounds();
parentLayer->buildProjectionUpToNode(device, this, requestedRect);
return device;
}
void KisTransformMask::recaclulateStaticImage()
{
/**
* Note: this function must be called from within the scheduler's
* context. We are accessing parent's updateProjection(), which
* is not entirely safe.
*/
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
KIS_ASSERT_RECOVER_RETURN(parentLayer);
if (!m_d->staticCacheDevice) {
m_d->staticCacheDevice =
new KisPaintDevice(parentLayer->original()->colorSpace());
}
m_d->recalculatingStaticImage = true;
/**
* updateProjection() is assuming that the requestedRect takes
* into account all the change rects of all the masks. Usually,
* this work is done by the walkers.
*/
QRect requestedRect = parentLayer->changeRect(parentLayer->original()->exactBounds());
/**
* Here we use updateProjection() to regenerate the projection of
* the layer and after that a special update call (no-filthy) will
* be issued to pass the changed further through the stack.
*/
parentLayer->updateProjection(requestedRect, this);
m_d->recalculatingStaticImage = false;
m_d->staticCacheValid = true;
}
QRect KisTransformMask::decorateRect(KisPaintDeviceSP &src,
KisPaintDeviceSP &dst,
const QRect & rc,
PositionToFilthy maskPos) const
{
Q_ASSERT_X(src != dst, "KisTransformMask::decorateRect",
"src must be != dst, because we cant create transactions "
"during merge, as it breaks reentrancy");
KIS_ASSERT_RECOVER(m_d->params) { return rc; }
if (m_d->params->isHidden()) return rc;
KIS_ASSERT_RECOVER_NOOP(maskPos == N_FILTHY ||
maskPos == N_ABOVE_FILTHY ||
maskPos == N_BELOW_FILTHY);
if (!m_d->recalculatingStaticImage &&
(maskPos == N_FILTHY || maskPos == N_ABOVE_FILTHY)) {
m_d->staticCacheValid = false;
emit initiateDelayedStaticUpdate();
}
if (m_d->recalculatingStaticImage) {
m_d->staticCacheDevice->clear();
m_d->params->transformDevice(const_cast<KisTransformMask*>(this), src, m_d->staticCacheDevice);
QRect updatedRect = m_d->staticCacheDevice->extent();
KisPainter::copyAreaOptimized(updatedRect.topLeft(), m_d->staticCacheDevice, dst, updatedRect);
#ifdef DEBUG_RENDERING
- qDebug() << "Recalculate" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
+ dbgKrita << "Recalculate" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
KIS_DUMP_DEVICE_2(src, DUMP_RECT, "recalc_src", "dd");
KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "recalc_dst", "dd");
#endif /* DEBUG_RENDERING */
} else if (!m_d->staticCacheValid && m_d->params->isAffine()) {
m_d->worker.runPartialDst(src, dst, rc);
#ifdef DEBUG_RENDERING
- qDebug() << "Partial" << name() << ppVar(src->exactBounds()) << ppVar(src->extent()) << ppVar(dst->exactBounds()) << ppVar(dst->extent()) << ppVar(rc);
+ dbgKrita << "Partial" << name() << ppVar(src->exactBounds()) << ppVar(src->extent()) << ppVar(dst->exactBounds()) << ppVar(dst->extent()) << ppVar(rc);
KIS_DUMP_DEVICE_2(src, DUMP_RECT, "partial_src", "dd");
KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "partial_dst", "dd");
#endif /* DEBUG_RENDERING */
} else if (m_d->staticCacheDevice) {
KisPainter::copyAreaOptimized(rc.topLeft(), m_d->staticCacheDevice, dst, rc);
#ifdef DEBUG_RENDERING
- qDebug() << "Fetch" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
+ dbgKrita << "Fetch" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
KIS_DUMP_DEVICE_2(src, DUMP_RECT, "fetch_src", "dd");
KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "fetch_dst", "dd");
#endif /* DEBUG_RENDERING */
}
KIS_ASSERT_RECOVER_NOOP(this->busyProgressIndicator());
this->busyProgressIndicator()->update();
return rc;
}
bool KisTransformMask::accept(KisNodeVisitor &v)
{
return v.visit(this);
}
void KisTransformMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
QRect KisTransformMask::changeRect(const QRect &rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
/**
* FIXME: This check of the emptiness should be done
* on the higher/lower level
*/
if (rect.isEmpty()) return rect;
if (!m_d->params->isAffine()) return rect;
QRect bounds;
QRect interestRect;
KisNodeSP parentNode = parent();
if (parentNode) {
bounds = parentNode->original()->defaultBounds()->bounds();
interestRect = parentNode->original()->extent();
} else {
bounds = QRect(0,0,777,777);
interestRect = QRect(0,0,888,888);
- qWarning() << "WARNING: transform mask has no parent (change rect)."
+ warnKrita << "WARNING: transform mask has no parent (change rect)."
<< "Cannot run safe transformations."
<< "Will limit bounds to" << ppVar(bounds);
}
const QRect limitingRect = KisAlgebra2D::blowRect(bounds, m_d->offBoundsReadArea);
KisSafeTransform transform(m_d->worker.forwardTransform(), limitingRect, interestRect);
QRect changeRect = transform.mapRectForward(rect);
return changeRect;
}
QRect KisTransformMask::needRect(const QRect& rect, PositionToFilthy pos) const
{
Q_UNUSED(pos);
/**
* FIXME: This check of the emptiness should be done
* on the higher/lower level
*/
if (rect.isEmpty()) return rect;
if (!m_d->params->isAffine()) return rect;
QRect bounds;
QRect interestRect;
KisNodeSP parentNode = parent();
if (parentNode) {
bounds = parentNode->original()->defaultBounds()->bounds();
interestRect = parentNode->original()->extent();
} else {
bounds = QRect(0,0,777,777);
interestRect = QRect(0,0,888,888);
- qWarning() << "WARNING: transform mask has no parent (need rect)."
+ warnKrita << "WARNING: transform mask has no parent (need rect)."
<< "Cannot run safe transformations."
<< "Will limit bounds to" << ppVar(bounds);
}
const QRect limitingRect = KisAlgebra2D::blowRect(bounds, m_d->offBoundsReadArea);
KisSafeTransform transform(m_d->worker.forwardTransform(), limitingRect, interestRect);
QRect needRect = transform.mapRectBackward(rect);
return needRect;
}
QRect KisTransformMask::extent() const
{
QRect rc = KisMask::extent();
QRect partialChangeRect;
QRect existentProjection;
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
if (parentLayer) {
partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
existentProjection = parentLayer->projection()->extent();
}
return changeRect(partialChangeRect) | existentProjection;
}
QRect KisTransformMask::exactBounds() const
{
QRect rc = KisMask::exactBounds();
QRect partialChangeRect;
QRect existentProjection;
KisLayerSP parentLayer = dynamic_cast<KisLayer*>(parent().data());
if (parentLayer) {
partialChangeRect = parentLayer->partialChangeRect(const_cast<KisTransformMask*>(this), rc);
existentProjection = parentLayer->projection()->exactBounds();
}
return changeRect(partialChangeRect) | existentProjection;
}
void KisTransformMask::setX(qint32 x)
{
m_d->params->translate(QPointF(x - this->x(), 0));
setTransformParams(m_d->params);
KisEffectMask::setX(x);
}
void KisTransformMask::setY(qint32 y)
{
m_d->params->translate(QPointF(0, y - this->y()));
setTransformParams(m_d->params);
KisEffectMask::setY(y);
}
diff --git a/krita/image/kis_transform_mask_params_interface.cpp b/krita/image/kis_transform_mask_params_interface.cpp
index 3a5acb7283e..4dcac880ca5 100644
--- a/krita/image/kis_transform_mask_params_interface.cpp
+++ b/krita/image/kis_transform_mask_params_interface.cpp
@@ -1,157 +1,157 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transform_mask_params_interface.h"
#include <QTransform>
KisTransformMaskParamsInterface::~KisTransformMaskParamsInterface()
{
}
///////////////// KisDumbTransformMaskParams ////////////////////////////
#include <QDomElement>
#include "kis_dom_utils.h"
#include "kis_node.h"
#include "kis_painter.h"
#include "KoCompositeOpRegistry.h"
struct Q_DECL_HIDDEN KisDumbTransformMaskParams::Private
{
Private() : isHidden(false) {}
QTransform transform;
bool isHidden;
};
KisDumbTransformMaskParams::KisDumbTransformMaskParams()
: m_d(new Private)
{
}
KisDumbTransformMaskParams::KisDumbTransformMaskParams(const QTransform &transform)
: m_d(new Private)
{
m_d->isHidden = false;
m_d->transform = transform;
}
KisDumbTransformMaskParams::KisDumbTransformMaskParams(bool isHidden)
: m_d(new Private)
{
m_d->isHidden = isHidden;
}
KisDumbTransformMaskParams::~KisDumbTransformMaskParams()
{
}
QTransform KisDumbTransformMaskParams::finalAffineTransform() const
{
return m_d->transform;
}
bool KisDumbTransformMaskParams::isAffine() const
{
return true;
}
bool KisDumbTransformMaskParams::isHidden() const
{
return m_d->isHidden;
}
void KisDumbTransformMaskParams::transformDevice(KisNodeSP node, KisPaintDeviceSP src, KisPaintDeviceSP dst) const
{
Q_UNUSED(node);
QRect rc = src->exactBounds();
QPoint dstTopLeft = rc.topLeft();
QTransform t = finalAffineTransform();
if (t.isTranslating()) {
dstTopLeft = t.map(dstTopLeft);
} else if (!t.isIdentity()) {
- qWarning() << "KisDumbTransformMaskParams::transformDevice does not support this kind of transformation";
- qWarning() << ppVar(t);
+ warnKrita << "KisDumbTransformMaskParams::transformDevice does not support this kind of transformation";
+ warnKrita << ppVar(t);
}
KisPainter::copyAreaOptimized(dstTopLeft, src, dst, rc);
}
QString KisDumbTransformMaskParams::id() const
{
return "dumbparams";
}
void KisDumbTransformMaskParams::toXML(QDomElement *e) const
{
QDomDocument doc = e->ownerDocument();
QDomElement transformEl = doc.createElement("dumb_transform");
e->appendChild(transformEl);
KisDomUtils::saveValue(&transformEl, "transform", m_d->transform);
}
KisTransformMaskParamsInterfaceSP KisDumbTransformMaskParams::fromXML(const QDomElement &e)
{
QDomElement transformEl;
bool result = false;
QTransform transform;
result =
KisDomUtils::findOnlyElement(e, "dumb_transform", &transformEl) &&
KisDomUtils::loadValue(transformEl, "transform", &transform);
if (!result) {
- qWarning() << "WARNING: couldn't load dumb transform. Ignoring...";
+ warnKrita << "WARNING: couldn't load dumb transform. Ignoring...";
}
return KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform));
}
void KisDumbTransformMaskParams::translate(const QPointF &offset)
{
m_d->transform *= QTransform::fromTranslate(offset.x(), offset.y());
}
QTransform KisDumbTransformMaskParams::testingGetTransform() const
{
return m_d->transform;
}
void KisDumbTransformMaskParams::testingSetTransform(const QTransform &t)
{
m_d->transform = t;
}
#include "kis_transform_mask_params_factory_registry.h"
struct DumbParamsRegistrar {
DumbParamsRegistrar() {
KisTransformMaskParamsFactory f(KisDumbTransformMaskParams::fromXML);
KisTransformMaskParamsFactoryRegistry::instance()->addFactory("dumbparams", f);
}
};
static DumbParamsRegistrar __dumbParamsRegistrar;
diff --git a/krita/image/kis_update_job_item.h b/krita/image/kis_update_job_item.h
index 35533602db3..b477d37eed3 100644
--- a/krita/image/kis_update_job_item.h
+++ b/krita/image/kis_update_job_item.h
@@ -1,198 +1,198 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_UPDATE_JOB_ITEM_H
#define __KIS_UPDATE_JOB_ITEM_H
#include <QRunnable>
#include <QReadWriteLock>
#include "kis_stroke_job.h"
#include "kis_spontaneous_job.h"
#include "kis_base_rects_walker.h"
#include "kis_async_merger.h"
class KisUpdateJobItem : public QObject, public QRunnable
{
Q_OBJECT
public:
enum Type {
EMPTY,
MERGE,
STROKE,
SPONTANEOUS
};
public:
KisUpdateJobItem(QReadWriteLock *exclusiveJobLock)
: m_exclusiveJobLock(exclusiveJobLock),
m_type(EMPTY)
{
setAutoDelete(false);
}
void run() {
if(m_exclusive) {
m_exclusiveJobLock->lockForWrite();
} else {
m_exclusiveJobLock->lockForRead();
}
if(m_type == MERGE) {
runMergeJob();
} else {
Q_ASSERT(m_type == STROKE || m_type == SPONTANEOUS);
m_runnableJob->run();
delete m_runnableJob;
}
setDone();
emit sigDoSomeUsefulWork();
emit sigJobFinished();
m_exclusiveJobLock->unlock();
}
inline void runMergeJob() {
Q_ASSERT(m_type == MERGE);
- // qDebug() << "Executing merge job" << m_walker->changeRect()
+ // dbgKrita << "Executing merge job" << m_walker->changeRect()
// << "on thread" << QThread::currentThreadId();
m_merger.startMerge(*m_walker);
QRect changeRect = m_walker->changeRect();
emit sigContinueUpdate(changeRect);
}
inline void setWalker(KisBaseRectsWalkerSP walker) {
m_type = MERGE;
m_accessRect = walker->accessRect();
m_changeRect = walker->changeRect();
m_walker = walker;
m_exclusive = false;
m_runnableJob = 0;
}
inline void setStrokeJob(KisStrokeJob *strokeJob) {
m_type = STROKE;
m_runnableJob = strokeJob;
m_exclusive = strokeJob->isExclusive();
m_walker = 0;
m_accessRect = m_changeRect = QRect();
}
inline void setSpontaneousJob(KisSpontaneousJob *spontaneousJob) {
m_type = SPONTANEOUS;
m_runnableJob = spontaneousJob;
m_exclusive = false;
m_walker = 0;
m_accessRect = m_changeRect = QRect();
}
inline void setDone() {
m_walker = 0;
m_runnableJob = 0;
m_type = EMPTY;
}
inline bool isRunning() const {
return m_type != EMPTY;
}
inline Type type() const {
return m_type;
}
inline const QRect& accessRect() const {
return m_accessRect;
}
inline const QRect& changeRect() const {
return m_changeRect;
}
Q_SIGNALS:
void sigContinueUpdate(const QRect& rc);
void sigDoSomeUsefulWork();
void sigJobFinished();
private:
/**
* Open walker and stroke job for the testing suite.
* Please, do not use it in production code.
*/
friend class KisSimpleUpdateQueueTest;
friend class KisStrokesQueueTest;
friend class KisUpdateSchedulerTest;
friend class KisTestableUpdaterContext;
inline KisBaseRectsWalkerSP walker() const {
return m_walker;
}
inline KisStrokeJob* strokeJob() const {
KisStrokeJob *job = dynamic_cast<KisStrokeJob*>(m_runnableJob);
Q_ASSERT(job);
return job;
}
inline void testingSetDone() {
if(m_type == STROKE) {
delete m_runnableJob;
}
setDone();
}
private:
/**
* \see KisUpdaterContext::m_exclusiveJobLock
*/
QReadWriteLock *m_exclusiveJobLock;
bool m_exclusive;
volatile Type m_type;
/**
* Runnable jobs part
* The job is owned by the context and deleted after completion
*/
KisRunnable *m_runnableJob;
/**
* Merge jobs part
*/
KisBaseRectsWalkerSP m_walker;
KisAsyncMerger m_merger;
/**
* These rects cache actual values from the walker
* to eliminate concurrent access to a walker structure
*/
QRect m_accessRect;
QRect m_changeRect;
};
#endif /* __KIS_UPDATE_JOB_ITEM_H */
diff --git a/krita/image/kis_update_scheduler.cpp b/krita/image/kis_update_scheduler.cpp
index 7d63acbf5f5..9f445b55cea 100644
--- a/krita/image/kis_update_scheduler.cpp
+++ b/krita/image/kis_update_scheduler.cpp
@@ -1,390 +1,390 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_update_scheduler.h"
#include "klocale.h"
#include "kis_image_config.h"
#include "kis_merge_walker.h"
#include "kis_full_refresh_walker.h"
#include "kis_updater_context.h"
#include "kis_simple_update_queue.h"
#include "kis_strokes_queue.h"
#include "kis_queues_progress_updater.h"
#include <QReadWriteLock>
#include "kis_lazy_wait_condition.h"
//#define DEBUG_BALANCING
#ifdef DEBUG_BALANCING
#define DEBUG_BALANCING_METRICS(decidedFirst, excl) \
- qDebug() << "Balance decision:" << decidedFirst \
+ dbgKrita << "Balance decision:" << decidedFirst \
<< "(" << excl << ")" \
<< "updates:" << m_d->updatesQueue->sizeMetric() \
<< "strokes:" << m_d->strokesQueue->sizeMetric()
#else
#define DEBUG_BALANCING_METRICS(decidedFirst, excl)
#endif
struct Q_DECL_HIDDEN KisUpdateScheduler::Private {
Private() : updatesQueue(0), strokesQueue(0),
updaterContext(0), processingBlocked(false),
balancingRatio(1.0),
projectionUpdateListener(0),
progressUpdater(0) {}
KisSimpleUpdateQueue *updatesQueue;
KisStrokesQueue *strokesQueue;
KisUpdaterContext *updaterContext;
bool processingBlocked;
qreal balancingRatio; // updates-queue-size/strokes-queue-size
KisProjectionUpdateListener *projectionUpdateListener;
KisQueuesProgressUpdater *progressUpdater;
QAtomicInt updatesLockCounter;
QReadWriteLock updatesStartLock;
KisLazyWaitCondition updatesFinishedCondition;
};
KisUpdateScheduler::KisUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener)
: m_d(new Private)
{
updateSettings();
m_d->projectionUpdateListener = projectionUpdateListener;
// The queue will update settings in a constructor itself
m_d->updatesQueue = new KisSimpleUpdateQueue();
m_d->strokesQueue = new KisStrokesQueue();
m_d->updaterContext = new KisUpdaterContext();
connectSignals();
}
KisUpdateScheduler::KisUpdateScheduler()
: m_d(new Private)
{
}
KisUpdateScheduler::~KisUpdateScheduler()
{
delete m_d->updaterContext;
delete m_d->updatesQueue;
delete m_d->strokesQueue;
delete m_d->progressUpdater;
}
void KisUpdateScheduler::connectSignals()
{
connect(m_d->updaterContext, SIGNAL(sigContinueUpdate(const QRect&)),
SLOT(continueUpdate(const QRect&)),
Qt::DirectConnection);
connect(m_d->updaterContext, SIGNAL(sigDoSomeUsefulWork()),
SLOT(doSomeUsefulWork()), Qt::DirectConnection);
connect(m_d->updaterContext, SIGNAL(sigSpareThreadAppeared()),
SLOT(spareThreadAppeared()), Qt::DirectConnection);
}
void KisUpdateScheduler::setProgressProxy(KoProgressProxy *progressProxy)
{
delete m_d->progressUpdater;
m_d->progressUpdater = progressProxy ?
new KisQueuesProgressUpdater(progressProxy) : 0;
}
void KisUpdateScheduler::progressUpdate()
{
if (!m_d->progressUpdater) return;
if(!m_d->strokesQueue->hasOpenedStrokes()) {
QString jobName = m_d->strokesQueue->currentStrokeName().toString();
if(jobName.isEmpty()) {
jobName = i18n("Updating...");
}
int sizeMetric = m_d->strokesQueue->sizeMetric();
if (!sizeMetric) {
sizeMetric = m_d->updatesQueue->sizeMetric();
}
m_d->progressUpdater->updateProgress(sizeMetric, jobName);
}
else {
m_d->progressUpdater->hide();
}
}
void KisUpdateScheduler::updateProjection(KisNodeSP node, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue->addUpdateJob(node, rc, cropRect);
processQueues();
}
void KisUpdateScheduler::updateProjectionNoFilthy(KisNodeSP node, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue->addUpdateNoFilthyJob(node, rc, cropRect);
processQueues();
}
void KisUpdateScheduler::fullRefreshAsync(KisNodeSP root, const QRect& rc, const QRect &cropRect)
{
m_d->updatesQueue->addFullRefreshJob(root, rc, cropRect);
processQueues();
}
void KisUpdateScheduler::fullRefresh(KisNodeSP root, const QRect& rc, const QRect &cropRect)
{
KisBaseRectsWalkerSP walker = new KisFullRefreshWalker(cropRect);
walker->collectRects(root, rc);
bool needLock = true;
if(m_d->processingBlocked) {
warnImage << "WARNING: Calling synchronous fullRefresh under a scheduler lock held";
warnImage << "We will not assert for now, but please port caller's to strokes";
warnImage << "to avoid this warning";
needLock = false;
}
if(needLock) lock();
m_d->updaterContext->lock();
Q_ASSERT(m_d->updaterContext->isJobAllowed(walker));
m_d->updaterContext->addMergeJob(walker);
m_d->updaterContext->waitForDone();
m_d->updaterContext->unlock();
if(needLock) unlock();
}
void KisUpdateScheduler::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{
m_d->updatesQueue->addSpontaneousJob(spontaneousJob);
processQueues();
}
KisStrokeId KisUpdateScheduler::startStroke(KisStrokeStrategy *strokeStrategy)
{
KisStrokeId id = m_d->strokesQueue->startStroke(strokeStrategy);
processQueues();
return id;
}
void KisUpdateScheduler::addJob(KisStrokeId id, KisStrokeJobData *data)
{
m_d->strokesQueue->addJob(id, data);
processQueues();
}
void KisUpdateScheduler::endStroke(KisStrokeId id)
{
m_d->strokesQueue->endStroke(id);
processQueues();
}
bool KisUpdateScheduler::cancelStroke(KisStrokeId id)
{
bool result = m_d->strokesQueue->cancelStroke(id);
processQueues();
return result;
}
bool KisUpdateScheduler::tryCancelCurrentStrokeAsync()
{
return m_d->strokesQueue->tryCancelCurrentStrokeAsync();
}
bool KisUpdateScheduler::wrapAroundModeSupported() const
{
return m_d->strokesQueue->wrapAroundModeSupported();
}
void KisUpdateScheduler::updateSettings()
{
if(m_d->updatesQueue) {
m_d->updatesQueue->updateSettings();
}
KisImageConfig config;
m_d->balancingRatio = config.schedulerBalancingRatio();
}
void KisUpdateScheduler::lock()
{
m_d->processingBlocked = true;
m_d->updaterContext->waitForDone();
}
void KisUpdateScheduler::unlock()
{
m_d->processingBlocked = false;
processQueues();
}
void KisUpdateScheduler::waitForDone()
{
do {
processQueues();
m_d->updaterContext->waitForDone();
} while(!m_d->updatesQueue->isEmpty() || !m_d->strokesQueue->isEmpty());
}
bool KisUpdateScheduler::tryBarrierLock()
{
if(!m_d->updatesQueue->isEmpty() || !m_d->strokesQueue->isEmpty())
return false;
m_d->processingBlocked = true;
m_d->updaterContext->waitForDone();
if(!m_d->updatesQueue->isEmpty() || !m_d->strokesQueue->isEmpty()) {
m_d->processingBlocked = false;
return false;
}
return true;
}
void KisUpdateScheduler::barrierLock()
{
do {
m_d->processingBlocked = false;
processQueues();
m_d->processingBlocked = true;
m_d->updaterContext->waitForDone();
} while(!m_d->updatesQueue->isEmpty() || !m_d->strokesQueue->isEmpty());
}
void KisUpdateScheduler::processQueues()
{
wakeUpWaitingThreads();
if(m_d->processingBlocked) return;
if(m_d->strokesQueue->needsExclusiveAccess()) {
DEBUG_BALANCING_METRICS("STROKES", "X");
m_d->strokesQueue->processQueue(*m_d->updaterContext,
!m_d->updatesQueue->isEmpty());
if(!m_d->strokesQueue->needsExclusiveAccess()) {
tryProcessUpdatesQueue();
}
}
else if(m_d->balancingRatio * m_d->strokesQueue->sizeMetric() > m_d->updatesQueue->sizeMetric()) {
DEBUG_BALANCING_METRICS("STROKES", "N");
m_d->strokesQueue->processQueue(*m_d->updaterContext,
!m_d->updatesQueue->isEmpty());
tryProcessUpdatesQueue();
}
else {
DEBUG_BALANCING_METRICS("UPDATES", "N");
tryProcessUpdatesQueue();
m_d->strokesQueue->processQueue(*m_d->updaterContext,
!m_d->updatesQueue->isEmpty());
}
progressUpdate();
}
void KisUpdateScheduler::blockUpdates()
{
m_d->updatesFinishedCondition.initWaiting();
m_d->updatesLockCounter.ref();
while(haveUpdatesRunning()) {
m_d->updatesFinishedCondition.wait();
}
m_d->updatesFinishedCondition.endWaiting();
}
void KisUpdateScheduler::unblockUpdates()
{
m_d->updatesLockCounter.deref();
processQueues();
}
void KisUpdateScheduler::wakeUpWaitingThreads()
{
if(m_d->updatesLockCounter && !haveUpdatesRunning()) {
m_d->updatesFinishedCondition.wakeAll();
}
}
void KisUpdateScheduler::tryProcessUpdatesQueue()
{
QReadLocker locker(&m_d->updatesStartLock);
if(m_d->updatesLockCounter) return;
m_d->updatesQueue->processQueue(*m_d->updaterContext);
}
bool KisUpdateScheduler::haveUpdatesRunning()
{
QWriteLocker locker(&m_d->updatesStartLock);
qint32 numMergeJobs, numStrokeJobs;
m_d->updaterContext->getJobsSnapshot(numMergeJobs, numStrokeJobs);
return numMergeJobs;
}
void KisUpdateScheduler::continueUpdate(const QRect &rect)
{
Q_ASSERT(m_d->projectionUpdateListener);
m_d->projectionUpdateListener->notifyProjectionUpdated(rect);
}
void KisUpdateScheduler::doSomeUsefulWork()
{
m_d->updatesQueue->optimize();
}
void KisUpdateScheduler::spareThreadAppeared()
{
processQueues();
}
KisTestableUpdateScheduler::KisTestableUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener,
qint32 threadCount)
{
updateSettings();
m_d->projectionUpdateListener = projectionUpdateListener;
// The queue will update settings in a constructor itself
m_d->updatesQueue = new KisTestableSimpleUpdateQueue();
m_d->strokesQueue = new KisStrokesQueue();
m_d->updaterContext = new KisTestableUpdaterContext(threadCount);
connectSignals();
}
KisTestableUpdaterContext* KisTestableUpdateScheduler::updaterContext()
{
return dynamic_cast<KisTestableUpdaterContext*>(m_d->updaterContext);
}
KisTestableSimpleUpdateQueue* KisTestableUpdateScheduler::updateQueue()
{
return dynamic_cast<KisTestableSimpleUpdateQueue*>(m_d->updatesQueue);
}
diff --git a/krita/image/kis_wrapped_line_iterator_base.h b/krita/image/kis_wrapped_line_iterator_base.h
index 5563a598a2a..acf57c9e2fe 100644
--- a/krita/image/kis_wrapped_line_iterator_base.h
+++ b/krita/image/kis_wrapped_line_iterator_base.h
@@ -1,157 +1,157 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_WRAPPED_LINE_ITERATOR_BASE_H
#define __KIS_WRAPPED_LINE_ITERATOR_BASE_H
template <class IteratorStrategy, class BaseClass>
class KisWrappedLineIteratorBase : public BaseClass
{
public:
KisWrappedLineIteratorBase(KisDataManager *dataManager,
const KisWrappedRect &splitRect,
qint32 offsetX, qint32 offsetY,
bool writable)
: m_splitRect(splitRect)
{
Q_ASSERT(m_splitRect.isSplit());
m_iterators.resize(4);
for (int i = 0; i < 4; i++) {
QRect rc = m_splitRect[i];
if (rc.isEmpty()) continue;
m_iterators[i] = m_strategy.createIterator(dataManager,
rc,
offsetX, offsetY,
writable);
}
m_strategy.completeInitialization(&m_iterators, &m_splitRect);
m_iterationAreaSize =
m_strategy.originalRectToColumnsRows(m_splitRect.originalRect());
m_currentIterator = m_strategy.leftColumnIterator();
}
bool nextPixel() {
int result = m_currentIterator->nextPixel();
if (!result) {
result = trySwitchColumn();
}
m_currentPos.rx()++;
return m_currentPos.rx() < m_iterationAreaSize.width();
}
bool nextPixels(qint32 n) {
int result = m_currentIterator->nextPixels(n);
if (!result) {
result = trySwitchColumn();
}
m_currentPos.rx() += n;
return m_currentPos.rx() < m_iterationAreaSize.width();
}
void nextRow() {
if (!m_strategy.trySwitchIteratorStripe()) {
m_strategy.iteratorsToNextRow();
}
m_currentIterator = m_strategy.leftColumnIterator();
m_currentPos.rx() = 0;
m_currentPos.ry()++;
}
void nextColumn() {
nextRow();
}
const quint8* oldRawData() const {
return m_currentIterator->oldRawData();
}
const quint8* rawDataConst() const {
return m_currentIterator->rawDataConst();
}
quint8* rawData() {
return m_currentIterator->rawData();
}
qint32 nConseqPixels() const {
qint32 iteratorChunk =
m_currentIterator->nConseqPixels();
return qMin(iteratorChunk,
m_iterationAreaSize.width() - m_currentPos.x());
}
qint32 x() const {
return (m_splitRect.originalRect().topLeft() +
m_strategy.columnRowToXY(m_currentPos)).x();
}
qint32 y() const {
return (m_splitRect.originalRect().topLeft() +
m_strategy.columnRowToXY(m_currentPos)).y();
}
void resetPixelPos() {
- qCritical() << "CRITICAL: resetPixelPos() is not implemented";
+ errKrita << "CRITICAL: resetPixelPos() is not implemented";
}
void resetRowPos() {
- qCritical() << "CRITICAL: resetRowPos() is not implemented";
+ errKrita << "CRITICAL: resetRowPos() is not implemented";
}
void resetColumnPos() {
resetRowPos();
}
private:
bool trySwitchColumn() {
int result = true;
if (m_currentIterator == m_strategy.leftColumnIterator() &&
m_strategy.rightColumnIterator()) {
m_currentIterator = m_strategy.rightColumnIterator();
} else if (m_strategy.trySwitchColumnForced()) {
m_currentIterator = m_strategy.leftColumnIterator();
} else {
result = false;
}
return result;
}
private:
KisWrappedRect m_splitRect;
QSize m_iterationAreaSize; // columns x rows
QPoint m_currentPos; // column, row
QVector<typename IteratorStrategy::IteratorTypeSP> m_iterators;
typename IteratorStrategy::IteratorTypeSP m_currentIterator;
IteratorStrategy m_strategy;
};
#endif /* __KIS_WRAPPED_LINE_ITERATOR_BASE_H */
diff --git a/krita/image/layerstyles/kis_layer_style_filter_projection_plane.cpp b/krita/image/layerstyles/kis_layer_style_filter_projection_plane.cpp
index 4ddb520aad6..ee9a3da2f8f 100644
--- a/krita/image/layerstyles/kis_layer_style_filter_projection_plane.cpp
+++ b/krita/image/layerstyles/kis_layer_style_filter_projection_plane.cpp
@@ -1,110 +1,110 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_style_filter_projection_plane.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_layer_style_filter.h"
#include "kis_layer_style_filter_environment.h"
#include "kis_psd_layer_style.h"
#include "kis_painter.h"
struct KisLayerStyleFilterProjectionPlane::Private
{
KisLayer *sourceLayer;
QScopedPointer<KisLayerStyleFilter> filter;
KisPSDLayerStyleSP style;
QScopedPointer<KisLayerStyleFilterEnvironment> environment;
};
KisLayerStyleFilterProjectionPlane::
KisLayerStyleFilterProjectionPlane(KisLayer *sourceLayer)
: m_d(new Private)
{
Q_ASSERT(sourceLayer);
m_d->sourceLayer = sourceLayer;
m_d->environment.reset(new KisLayerStyleFilterEnvironment(sourceLayer));
}
KisLayerStyleFilterProjectionPlane::~KisLayerStyleFilterProjectionPlane()
{
}
void KisLayerStyleFilterProjectionPlane::setStyle(KisLayerStyleFilter *filter, KisPSDLayerStyleSP style)
{
m_d->filter.reset(filter);
m_d->style = style;
}
QRect KisLayerStyleFilterProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
{
Q_UNUSED(rect);
Q_UNUSED(filthyNode);
/// do nothing
return QRect();
}
void KisLayerStyleFilterProjectionPlane::apply(KisPainter *painter, const QRect &rect)
{
if (!m_d->sourceLayer || !m_d->filter) {
- qWarning() << "KisLayerStyleFilterProjectionPlane::apply(): [BUG] is not initialized";
+ warnKrita << "KisLayerStyleFilterProjectionPlane::apply(): [BUG] is not initialized";
return;
}
m_d->filter->processDirectly(m_d->sourceLayer->projection(),
painter->device(),
rect,
m_d->style,
m_d->environment.data());
}
QRect KisLayerStyleFilterProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
if (!m_d->sourceLayer || !m_d->filter) {
- qWarning() << "KisLayerStyleFilterProjectionPlane::needRect(): [BUG] is not initialized";
+ warnKrita << "KisLayerStyleFilterProjectionPlane::needRect(): [BUG] is not initialized";
return rect;
}
KIS_ASSERT_RECOVER_NOOP(pos == KisLayer::N_ABOVE_FILTHY);
return m_d->filter->neededRect(rect, m_d->style);
}
QRect KisLayerStyleFilterProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
if (!m_d->sourceLayer || !m_d->filter) {
- qWarning() << "KisLayerStyleFilterProjectionPlane::changeRect(): [BUG] is not initialized";
+ warnKrita << "KisLayerStyleFilterProjectionPlane::changeRect(): [BUG] is not initialized";
return rect;
}
KIS_ASSERT_RECOVER_NOOP(pos == KisLayer::N_ABOVE_FILTHY);
return m_d->filter->changedRect(rect, m_d->style);
}
QRect KisLayerStyleFilterProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
{
return needRect(rect, pos);
}
diff --git a/krita/image/layerstyles/kis_ls_bevel_emboss_filter.cpp b/krita/image/layerstyles/kis_ls_bevel_emboss_filter.cpp
index 0b342390a9c..d05deded636 100644
--- a/krita/image/layerstyles/kis_ls_bevel_emboss_filter.cpp
+++ b/krita/image/layerstyles/kis_ls_bevel_emboss_filter.cpp
@@ -1,478 +1,478 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* See LayerFX plugin for Gimp as a reference implementation of this style:
* http://registry.gimp.org/node/186
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_ls_bevel_emboss_filter.h"
#include <cstdlib>
#include <QBitArray>
#include <KoUpdater.h>
#include <KoPattern.h>
#include <KoAbstractGradient.h>
#include "psd.h"
#include "kis_convolution_kernel.h"
#include "kis_convolution_painter.h"
#include "kis_gaussian_kernel.h"
#include "kis_pixel_selection.h"
#include "kis_fill_painter.h"
#include "kis_gradient_painter.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "kis_psd_layer_style.h"
#include "kis_layer_style_filter_environment.h"
#include "kis_ls_utils.h"
#include "gimp_bump_map.h"
#include "kis_transaction.h"
KisLsBevelEmbossFilter::KisLsBevelEmbossFilter()
: KisLayerStyleFilter(KoID("lsstroke", i18n("Stroke (style)")))
{
}
void paintBevelSelection(KisPixelSelectionSP srcSelection,
KisPixelSelectionSP dstSelection,
const QRect &applyRect,
int size,
int initialSize,
bool invert)
{
KisSelectionSP tmpBaseSelection = new KisSelection(new KisSelectionEmptyBounds(0));
KisPixelSelectionSP tmpSelection = tmpBaseSelection->pixelSelection();
// NOTE: we are not using createCompositionSourceDevice() intentionally,
// because the source device doesn't have alpha channel
KisPixelSelectionSP fillDevice = new KisPixelSelection();
KisPainter gc(dstSelection);
gc.setCompositeOp(COMPOSITE_COPY);
for (int i = 0; i < size; i++) {
const int growSize = initialSize - i - 1;
quint8 selectedness = invert ?
qRound(qreal(size - i - 1) / size * 255.0) :
qRound(qreal(i + 1) / size * 255.0);
fillDevice->setDefaultPixel(&selectedness);
tmpSelection->makeCloneFromRough(srcSelection, srcSelection->selectedRect());
QRect changeRect = KisLsUtils::growSelectionUniform(tmpSelection, growSize, applyRect);
gc.setSelection(tmpBaseSelection);
gc.bitBlt(changeRect.topLeft(), fillDevice, changeRect);
}
}
struct ContrastOp {
static const bool supportsCaching = false;
ContrastOp(qreal contrast)
: m_contrast(contrast)
{
}
int operator() (int iValue) {
qreal value = qreal(iValue - 127) / 127.0;
qreal slant = std::tan ((m_contrast + 1) * M_PI_4);
value = (value - 0.5) * slant + 0.5;
return qRound(value * 255.0);
}
private:
qreal m_contrast;
};
struct HighlightsFetchOp {
static const bool supportsCaching = true;
int operator() (int value) {
return qRound(qMax(0, value - 127) * (255.0 / (255 - 127)));
}
};
struct ShadowsFetchOp {
static const bool supportsCaching = true;
int operator() (int value) {
return 255 - qRound(qMin(value, 127) * (255.0 / 127.0));
}
};
template <class MapOp>
void mapPixelValues(KisPixelSelectionSP srcSelection,
KisPixelSelectionSP dstSelection,
MapOp mapOp,
const QRect &applyRect)
{
static quint8 mapTable[256];
static bool mapInitialized = false;
if (!MapOp::supportsCaching || !mapInitialized) {
mapInitialized = true;
for (int i = 0; i < 256; i++) {
mapTable[i] = mapOp(i);
}
}
KisSequentialConstIterator srcIt(srcSelection, applyRect);
KisSequentialIterator dstIt(dstSelection, applyRect);
do {
const quint8 *srcPtr = srcIt.rawDataConst();
quint8 *dstPtr = dstIt.rawData();
*dstPtr = mapTable[*srcPtr];
} while(srcIt.nextPixel() && dstIt.nextPixel());
}
template <class MapOp>
void mapPixelValues(KisPixelSelectionSP dstSelection,
MapOp mapOp,
const QRect &applyRect)
{
static quint8 mapTable[256];
static bool mapInitialized = false;
if (!MapOp::supportsCaching || !mapInitialized) {
mapInitialized = true;
for (int i = 0; i < 256; i++) {
mapTable[i] = mapOp(i);
}
}
KisSequentialIterator dstIt(dstSelection, applyRect);
do {
quint8 *dstPtr = dstIt.rawData();
*dstPtr = mapTable[*dstPtr];
} while(dstIt.nextPixel());
}
struct BevelEmbossRectCalculator
{
BevelEmbossRectCalculator(const QRect &applyRect,
const psd_layer_effects_bevel_emboss *config) {
shadowHighlightsFinalRect = applyRect;
applyGaussianRect = shadowHighlightsFinalRect;
applyGlossContourRect = KisLsUtils::growRectFromRadius(applyGaussianRect, config->soften());
applyBumpmapRect = applyGlossContourRect;
applyContourRect = applyBumpmapRect;
applyTextureRect = applyContourRect;
applyBevelRect = calcBevelNeedRect(applyTextureRect, config);
initialFetchRect = kisGrowRect(applyBevelRect, 1);
}
QRect totalChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
QRect changeRect = calcBevelChangeRect(applyRect, config);
changeRect = kisGrowRect(changeRect, 1); // bumpmap method
changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften());
return changeRect;
}
QRect totalNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
QRect changeRect = applyRect;
changeRect = KisLsUtils::growRectFromRadius(changeRect, config->soften());
changeRect = kisGrowRect(changeRect, 1); // bumpmap method
changeRect = calcBevelNeedRect(applyRect, config);
return changeRect;
}
QRect initialFetchRect;
QRect applyBevelRect;
QRect applyTextureRect;
QRect applyContourRect;
QRect applyBumpmapRect;
QRect applyGlossContourRect;
QRect applyGaussianRect;
QRect shadowHighlightsFinalRect;
private:
QRect calcBevelChangeRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
const int size = config->size();
int limitingGrowSize = 0;
switch (config->style()) {
case psd_bevel_outer_bevel:
limitingGrowSize = size;
break;
case psd_bevel_inner_bevel:
limitingGrowSize = 0;
break;
case psd_bevel_emboss: {
const int initialSize = std::ceil(qreal(size) / 2.0);
limitingGrowSize = initialSize;
break;
}
case psd_bevel_pillow_emboss: {
const int halfSizeC = std::ceil(qreal(size) / 2.0);
limitingGrowSize = halfSizeC;
break;
}
case psd_bevel_stroke_emboss:
- qWarning() << "WARNING: Stroke Emboss style is not implemented yet!";
+ warnKrita << "WARNING: Stroke Emboss style is not implemented yet!";
return applyRect;
}
return kisGrowRect(applyRect, limitingGrowSize);
}
QRect calcBevelNeedRect(const QRect &applyRect, const psd_layer_effects_bevel_emboss *config) {
const int size = config->size();
int limitingGrowSize = size;
return kisGrowRect(applyRect, limitingGrowSize);
}
};
void KisLsBevelEmbossFilter::applyBevelEmboss(KisPaintDeviceSP srcDevice,
KisPaintDeviceSP dstDevice,
const QRect &applyRect,
const psd_layer_effects_bevel_emboss *config,
KisLayerStyleFilterEnvironment *env) const
{
if (applyRect.isEmpty()) return;
BevelEmbossRectCalculator d(applyRect, config);
KisSelectionSP baseSelection = KisLsUtils::selectionFromAlphaChannel(srcDevice, d.initialFetchRect);
KisPixelSelectionSP selection = baseSelection->pixelSelection();
//selection->convertToQImage(0, QRect(0,0,300,300)).save("0_selection_initial.png");
const int size = config->size();
int limitingGrowSize = 0;
KisPixelSelectionSP bumpmapSelection = new KisPixelSelection(new KisSelectionEmptyBounds(0));
switch (config->style()) {
case psd_bevel_outer_bevel:
paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, size, false);
limitingGrowSize = size;
break;
case psd_bevel_inner_bevel:
paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, 0, false);
limitingGrowSize = 0;
break;
case psd_bevel_emboss: {
const int initialSize = std::ceil(qreal(size) / 2.0);
paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, size, initialSize, false);
limitingGrowSize = initialSize;
break;
}
case psd_bevel_pillow_emboss: {
const int halfSizeF = std::floor(qreal(size) / 2.0);
const int halfSizeC = std::ceil(qreal(size) / 2.0);
// TODO: probably not correct!
paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeC, halfSizeC, false);
paintBevelSelection(selection, bumpmapSelection, d.applyBevelRect, halfSizeF, 0, true);
limitingGrowSize = halfSizeC;
break;
}
case psd_bevel_stroke_emboss:
- qWarning() << "WARNING: Stroke Emboss style is not implemented yet!";
+ warnKrita << "WARNING: Stroke Emboss style is not implemented yet!";
return;
}
KisPixelSelectionSP limitingSelection = new KisPixelSelection(*selection);
{
QRect changeRectUnused =
KisLsUtils::growSelectionUniform(limitingSelection,
limitingGrowSize,
d.applyBevelRect);
Q_UNUSED(changeRectUnused);
}
//bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("1_selection_xconv.png");
if (config->textureEnabled()) {
KisPixelSelectionSP textureSelection = new KisPixelSelection(new KisSelectionEmptyBounds(0));
KisLsUtils::fillPattern(textureSelection, d.applyTextureRect, env,
config->textureScale(),
config->texturePattern(),
config->textureHorizontalPhase(),
config->textureVerticalPhase(),
config->textureAlignWithLayer());
int contrastadj = 0;
{
using namespace std;
int tex_depth = config->textureDepth();
if (tex_depth >= 0.0) {
if (tex_depth <= 100.0) {
contrastadj = int(qRound((1-(tex_depth/100.0)) * -127));
} else {
contrastadj = int(qRound(((tex_depth-100.0)/900.0) * 127));
}
} else {
textureSelection->invert();
if (tex_depth >= -100.0) {
contrastadj = int(qRound((1-(abs(tex_depth)/100.0)) * -127));
} else {
contrastadj = int(qRound(((abs(tex_depth)-100.0)/900.0) * 127));
}
}
}
qreal contrast = qBound(-1.0, qreal(contrastadj) / 127.0, 1.0);
mapPixelValues(textureSelection, ContrastOp(contrast), d.applyTextureRect);
{
KisPainter gc(bumpmapSelection);
gc.setCompositeOp(COMPOSITE_MULT);
gc.bitBlt(d.applyTextureRect.topLeft(), textureSelection, d.applyTextureRect);
gc.end();
}
}
//bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("15_selection_texture.png");
if (config->contourEnabled()) {
if (config->range() != KisLsUtils::FULL_PERCENT_RANGE) {
KisLsUtils::adjustRange(bumpmapSelection, d.applyContourRect, config->range());
}
KisLsUtils::applyContourCorrection(bumpmapSelection,
d.applyContourRect,
config->contourLookupTable(),
config->antiAliased(),
true);
}
bumpmap_vals_t bmvals;
bmvals.azimuth = config->angle();
bmvals.elevation = config->altitude();
bmvals.depth = config->depth();
bmvals.ambient = 0;
bmvals.compensate = true;
bmvals.invert = config->direction() == psd_direction_down;
bmvals.type = LINEAR;
bumpmap(bumpmapSelection, d.applyBumpmapRect, bmvals);
//bumpmapSelection->convertToQImage(0, QRect(0,0,300,300)).save("3_selection_bumpmap.png");
{ // TODO: optimize!
KisLsUtils::applyContourCorrection(bumpmapSelection,
d.applyGlossContourRect,
config->glossContourLookupTable(),
config->glossAntiAliased(),
true);
}
if (config->soften()) {
KisLsUtils::applyGaussian(bumpmapSelection, d.applyGaussianRect, config->soften());
}
if (config->textureEnabled() && config->textureInvert()) {
bumpmapSelection->invert();
}
selection->clear();
mapPixelValues(bumpmapSelection, selection,
ShadowsFetchOp(), d.shadowHighlightsFinalRect);
selection->applySelection(limitingSelection, SELECTION_INTERSECT);
//dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("4_dst_before_apply.png");
//selection->convertToQImage(0, QRect(0,0,300,300)).save("4_shadows_sel.png");
{
const KoColor fillColor(config->shadowColor(), dstDevice->colorSpace());
const QRect &fillRect = d.shadowHighlightsFinalRect;
KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace());
fillDevice->setDefaultPixel(fillColor.data());
KisPainter gc(dstDevice);
gc.setSelection(baseSelection);
gc.setCompositeOp(config->shadowBlendMode());
env->setupFinalPainter(&gc, config->shadowOpacity(), QBitArray());
gc.bitBlt(fillRect.topLeft(), fillDevice, fillRect);
gc.end();
}
selection->clear();
mapPixelValues(bumpmapSelection, selection,
HighlightsFetchOp(), d.shadowHighlightsFinalRect);
selection->applySelection(limitingSelection, SELECTION_INTERSECT);
//selection->convertToQImage(0, QRect(0,0,300,300)).save("5_highlights_sel.png");
{
const KoColor fillColor(config->highlightColor(), dstDevice->colorSpace());
const QRect &fillRect = d.shadowHighlightsFinalRect;
KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace());
fillDevice->setDefaultPixel(fillColor.data());
KisPainter gc(dstDevice);
gc.setSelection(baseSelection);
gc.setCompositeOp(config->highlightBlendMode());
env->setupFinalPainter(&gc, config->highlightOpacity(), QBitArray());
gc.bitBlt(fillRect.topLeft(), fillDevice, fillRect);
gc.end();
}
}
void KisLsBevelEmbossFilter::processDirectly(KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &applyRect,
KisPSDLayerStyleSP style,
KisLayerStyleFilterEnvironment *env) const
{
Q_UNUSED(env);
KIS_ASSERT_RECOVER_RETURN(style);
const psd_layer_effects_bevel_emboss *config = style->bevelAndEmboss();
if (!config->effectEnabled()) return;
applyBevelEmboss(src, dst, applyRect, config, env);
}
QRect KisLsBevelEmbossFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style) const
{
BevelEmbossRectCalculator d(rect, style->bevelAndEmboss());
return d.totalNeedRect(rect, style->bevelAndEmboss());
}
QRect KisLsBevelEmbossFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style) const
{
BevelEmbossRectCalculator d(rect, style->bevelAndEmboss());
return d.totalChangeRect(rect, style->bevelAndEmboss());
}
diff --git a/krita/image/layerstyles/kis_ls_drop_shadow_filter.cpp b/krita/image/layerstyles/kis_ls_drop_shadow_filter.cpp
index cbfed5ab06a..6e105327b64 100644
--- a/krita/image/layerstyles/kis_ls_drop_shadow_filter.cpp
+++ b/krita/image/layerstyles/kis_ls_drop_shadow_filter.cpp
@@ -1,276 +1,276 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_ls_drop_shadow_filter.h"
#include <cstdlib>
#include <QBitArray>
#include <KoUpdater.h>
#include <KoAbstractGradient.h>
#include "psd.h"
#include "kis_convolution_kernel.h"
#include "kis_convolution_painter.h"
#include "kis_gaussian_kernel.h"
#include "kis_pixel_selection.h"
#include "kis_fill_painter.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "kis_psd_layer_style.h"
#include "kis_ls_utils.h"
KisLsDropShadowFilter::KisLsDropShadowFilter(Mode mode)
: KisLayerStyleFilter(KoID("lsdropshadow", i18n("Drop Shadow (style)")))
, m_mode(mode)
{
}
struct ShadowRectsData
{
enum Direction {
NEED_RECT,
CHANGE_RECT
};
ShadowRectsData(const QRect &applyRect,
const psd_layer_effects_context *context,
const psd_layer_effects_shadow_base *shadow,
Direction direction)
{
spread_size = (shadow->spread() * shadow->size() + 50) / 100;
blur_size = shadow->size() - spread_size;
offset = shadow->calculateOffset(context);
// need rect calculation in reverse order
dstRect = applyRect;
const int directionCoeff = direction == NEED_RECT ? -1 : 1;
srcRect = dstRect.translated(directionCoeff * offset);
noiseNeedRect = shadow->noise() > 0 ?
kisGrowRect(srcRect, KisLsUtils::noiseNeedBorder) : srcRect;
blurNeedRect = blur_size ?
KisLsUtils::growRectFromRadius(noiseNeedRect, blur_size) : noiseNeedRect;
spreadNeedRect = spread_size ?
KisLsUtils::growRectFromRadius(blurNeedRect, spread_size) : blurNeedRect;
- // qDebug() << ppVar(dstRect);
- // qDebug() << ppVar(srcRect);
- // qDebug() << ppVar(noiseNeedRect);
- // qDebug() << ppVar(blurNeedRect);
- // qDebug() << ppVar(spreadNeedRect);
+ // dbgKrita << ppVar(dstRect);
+ // dbgKrita << ppVar(srcRect);
+ // dbgKrita << ppVar(noiseNeedRect);
+ // dbgKrita << ppVar(blurNeedRect);
+ // dbgKrita << ppVar(spreadNeedRect);
}
inline QRect finalNeedRect() const {
return spreadNeedRect;
}
inline QRect finalChangeRect() const {
// TODO: is it correct?
return spreadNeedRect;
}
qint32 spread_size;
qint32 blur_size;
QPoint offset;
QRect srcRect;
QRect dstRect;
QRect noiseNeedRect;
QRect blurNeedRect;
QRect spreadNeedRect;
};
void applyDropShadow(KisPaintDeviceSP srcDevice,
KisPaintDeviceSP dstDevice,
const QRect &applyRect,
const psd_layer_effects_context *context,
const psd_layer_effects_shadow_base *shadow,
const KisLayerStyleFilterEnvironment *env)
{
if (applyRect.isEmpty()) return;
ShadowRectsData d(applyRect, context, shadow, ShadowRectsData::NEED_RECT);
KisSelectionSP baseSelection =
KisLsUtils::selectionFromAlphaChannel(srcDevice, d.spreadNeedRect);
KisPixelSelectionSP selection = baseSelection->pixelSelection();
//selection->convertToQImage(0, QRect(0,0,300,300)).save("0_selection_initial.png");
if (shadow->invertsSelection()) {
selection->invert();
}
/**
* Copy selection which will be erased from the original later
*/
KisPixelSelectionSP knockOutSelection;
if (shadow->knocksOut()) {
knockOutSelection = new KisPixelSelection(*selection);
}
if (shadow->technique() == psd_technique_precise) {
KisLsUtils::findEdge(selection, d.blurNeedRect, true);
}
/**
* Spread and blur the selection
*/
if (d.spread_size) {
KisLsUtils::applyGaussian(selection, d.blurNeedRect, d.spread_size);
// TODO: find out why in libpsd we pass false here. If we do so,
// the result is fully black, which is not expected
KisLsUtils::findEdge(selection, d.blurNeedRect, true /*shadow->edgeHidden()*/);
}
//selection->convertToQImage(0, QRect(0,0,300,300)).save("1_selection_spread.png");
if (d.blur_size) {
KisLsUtils::applyGaussian(selection, d.noiseNeedRect, d.blur_size);
}
//selection->convertToQImage(0, QRect(0,0,300,300)).save("2_selection_blur.png");
if (shadow->range() != KisLsUtils::FULL_PERCENT_RANGE) {
KisLsUtils::adjustRange(selection, d.noiseNeedRect, shadow->range());
}
const psd_layer_effects_inner_glow *iglow = 0;
if ((iglow =
dynamic_cast<const psd_layer_effects_inner_glow *>(shadow)) &&
iglow->source() == psd_glow_center) {
selection->invert();
}
/**
* Contour correction
*/
KisLsUtils::applyContourCorrection(selection,
d.noiseNeedRect,
shadow->contourLookupTable(),
shadow->antiAliased(),
shadow->edgeHidden());
//selection->convertToQImage(0, QRect(0,0,300,300)).save("3_selection_contour.png");
/**
* Noise
*/
if (shadow->noise() > 0) {
KisLsUtils::applyNoise(selection,
d.srcRect,
shadow->noise(),
context);
}
//selection->convertToQImage(0, QRect(0,0,300,300)).save("4_selection_noise.png");
if (!d.offset.isNull()) {
selection->setX(d.offset.x());
selection->setY(d.offset.y());
}
/**
* Knock-out original outline of the device from the resulting shade
*/
if (shadow->knocksOut()) {
KIS_ASSERT_RECOVER_RETURN(knockOutSelection);
QRect knockOutRect = !shadow->invertsSelection() ?
d.srcRect : d.spreadNeedRect;
knockOutRect &= d.dstRect;
KisPainter gc(selection);
gc.setCompositeOp(COMPOSITE_ERASE);
gc.bitBlt(knockOutRect.topLeft(), knockOutSelection, knockOutRect);
}
//selection->convertToQImage(0, QRect(0,0,300,300)).save("5_selection_knockout.png");
KisLsUtils::applyFinalSelection(baseSelection,
srcDevice,
dstDevice,
d.srcRect,
d.dstRect,
context,
shadow,
env);
}
const psd_layer_effects_shadow_base*
KisLsDropShadowFilter::getShadowStruct(KisPSDLayerStyleSP style) const
{
const psd_layer_effects_shadow_base *config = 0;
if (m_mode == DropShadow) {
config = style->dropShadow();
} else if (m_mode == InnerShadow) {
config = style->innerShadow();
} else if (m_mode == OuterGlow) {
config = style->outerGlow();
} else if (m_mode == InnerGlow) {
config = style->innerGlow();
}
return config;
}
void KisLsDropShadowFilter::processDirectly(KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &applyRect,
KisPSDLayerStyleSP style,
KisLayerStyleFilterEnvironment *env) const
{
KIS_ASSERT_RECOVER_RETURN(style);
const psd_layer_effects_shadow_base *shadowStruct = getShadowStruct(style);
if (!shadowStruct->effectEnabled()) return;
applyDropShadow(src, dst, applyRect, style->context(), shadowStruct, env);
}
QRect KisLsDropShadowFilter::neededRect(const QRect &rect, KisPSDLayerStyleSP style) const
{
const psd_layer_effects_shadow_base *shadowStruct = getShadowStruct(style);
if (!shadowStruct->effectEnabled()) return rect;
ShadowRectsData d(rect, style->context(), shadowStruct, ShadowRectsData::NEED_RECT);
return rect | d.finalNeedRect();
}
QRect KisLsDropShadowFilter::changedRect(const QRect &rect, KisPSDLayerStyleSP style) const
{
const psd_layer_effects_shadow_base *shadowStruct = getShadowStruct(style);
if (!shadowStruct->effectEnabled()) return rect;
ShadowRectsData d(rect, style->context(), shadowStruct, ShadowRectsData::CHANGE_RECT);
return style->context()->keep_original ?
d.finalChangeRect() : rect | d.finalChangeRect();
}
diff --git a/krita/image/layerstyles/kis_ls_utils.cpp b/krita/image/layerstyles/kis_ls_utils.cpp
index c8535501f86..4317ca54223 100644
--- a/krita/image/layerstyles/kis_ls_utils.cpp
+++ b/krita/image/layerstyles/kis_ls_utils.cpp
@@ -1,625 +1,625 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_ls_utils.h"
#include <KoAbstractGradient.h>
#include <KoColorSpace.h>
#include <KoPattern.h>
#include "psd.h"
#include "kis_default_bounds.h"
#include "kis_pixel_selection.h"
#include "kis_random_accessor_ng.h"
#include "kis_iterator_ng.h"
#include "kis_convolution_kernel.h"
#include "kis_convolution_painter.h"
#include "kis_gaussian_kernel.h"
#include "kis_fill_painter.h"
#include "kis_gradient_painter.h"
#include "kis_layer_style_filter_environment.h"
#include "kis_selection_filters.h"
namespace KisLsUtils
{
QRect growSelectionUniform(KisPixelSelectionSP selection, int growSize, const QRect &applyRect)
{
QRect changeRect = applyRect;
if (growSize > 0) {
KisGrowSelectionFilter filter(growSize, growSize);
changeRect = filter.changeRect(applyRect);
filter.process(selection, applyRect);
} else if (growSize < 0) {
KisShrinkSelectionFilter filter(qAbs(growSize), qAbs(growSize), false);
changeRect = filter.changeRect(applyRect);
filter.process(selection, applyRect);
}
return changeRect;
}
KisSelectionSP selectionFromAlphaChannel(KisPaintDeviceSP device,
const QRect &srcRect)
{
const KoColorSpace *cs = device->colorSpace();
KisSelectionSP baseSelection = new KisSelection(new KisSelectionEmptyBounds(0));
KisPixelSelectionSP selection = baseSelection->pixelSelection();
KisSequentialConstIterator srcIt(device, srcRect);
KisSequentialIterator dstIt(selection, srcRect);
do {
quint8 *dstPtr = dstIt.rawData();
const quint8* srcPtr = srcIt.rawDataConst();
*dstPtr = cs->opacityU8(srcPtr);
} while(srcIt.nextPixel() && dstIt.nextPixel());
return baseSelection;
}
void findEdge(KisPixelSelectionSP selection, const QRect &applyRect, const bool edgeHidden)
{
KisSequentialIterator dstIt(selection, applyRect);
if (edgeHidden) {
do {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr =
(*pixelPtr < 24) ?
*pixelPtr * 10 : 0xFF;
} while(dstIt.nextPixel());
} else {
do {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = 0xFF;
} while(dstIt.nextPixel());
}
}
QRect growRectFromRadius(const QRect &rc, int radius)
{
int halfSize = KisGaussianKernel::kernelSizeFromRadius(radius) / 2;
return rc.adjusted(-halfSize, -halfSize, halfSize, halfSize);
}
void applyGaussian(KisPixelSelectionSP selection,
const QRect &applyRect,
qreal radius)
{
KisGaussianKernel::applyGaussian(selection, applyRect,
radius, radius,
QBitArray(), 0);
}
namespace Private {
KisPixelSelectionSP generateRandomSelection(const QRect &rc)
{
KisPixelSelectionSP selection = new KisPixelSelection();
KisSequentialIterator dstIt(selection, rc);
if (RAND_MAX >= 0x00FFFFFF) {
do {
int randValue = qrand();
*dstIt.rawData() = (quint8) randValue;
if (!dstIt.nextPixel()) break;
randValue >>= 8;
*dstIt.rawData() = (quint8) randValue;
if (!dstIt.nextPixel()) break;
randValue >>= 8;
*dstIt.rawData() = (quint8) randValue;
} while(dstIt.nextPixel());
} else {
do {
*dstIt.rawData() = (quint8) rand();
} while(dstIt.nextPixel());
}
return selection;
}
void getGradientTable(const KoAbstractGradient *gradient,
QVector<KoColor> *table,
const KoColorSpace *colorSpace)
{
KIS_ASSERT_RECOVER_RETURN(table->size() == 256);
for (int i = 0; i < 256; i++) {
gradient->colorAt(((*table)[i]), qreal(i) / 255.0);
(*table)[i].convertTo(colorSpace);
}
}
struct LinearGradientIndex
{
int popOneIndex(int selectionAlpha) {
return 255 - selectionAlpha;
}
bool nextPixel() {
return true;
}
};
struct JitterGradientIndex
{
JitterGradientIndex(const QRect &applyRect, int jitter)
: randomSelection(generateRandomSelection(applyRect)),
noiseIt(randomSelection, applyRect),
m_jitterCoeff(jitter * 255 / 100)
{
}
int popOneIndex(int selectionAlpha) {
int gradientIndex = 255 - selectionAlpha;
gradientIndex += m_jitterCoeff * *noiseIt.rawDataConst() >> 8;
gradientIndex &= 0xFF;
return gradientIndex;
}
bool nextPixel() {
return noiseIt.nextPixel();
}
private:
KisPixelSelectionSP randomSelection;
KisSequentialConstIterator noiseIt;
int m_jitterCoeff;
};
template <class IndexFetcher>
void applyGradientImpl(KisPaintDeviceSP device,
KisPixelSelectionSP selection,
const QRect &applyRect,
const QVector<KoColor> &table,
bool edgeHidden,
IndexFetcher &indexFetcher)
{
KIS_ASSERT_RECOVER_RETURN(
*table.first().colorSpace() == *device->colorSpace());
const KoColorSpace *cs = device->colorSpace();
const int pixelSize = cs->pixelSize();
KisSequentialConstIterator selIt(selection, applyRect);
KisSequentialIterator dstIt(device, applyRect);
if (edgeHidden) {
do {
quint8 selAlpha = *selIt.rawDataConst();
int gradientIndex = indexFetcher.popOneIndex(selAlpha);
const KoColor &color = table[gradientIndex];
quint8 tableAlpha = color.opacityU8();
memcpy(dstIt.rawData(), color.data(), pixelSize);
if (selAlpha < 24 && tableAlpha == 255) {
tableAlpha = int(selAlpha) * 10 * tableAlpha >> 8;
cs->setOpacity(dstIt.rawData(), tableAlpha, 1);
}
} while(selIt.nextPixel() &&
dstIt.nextPixel() &&
indexFetcher.nextPixel());
} else {
do {
int gradientIndex = indexFetcher.popOneIndex(*selIt.rawDataConst());
const KoColor &color = table[gradientIndex];
memcpy(dstIt.rawData(), color.data(), pixelSize);
} while(selIt.nextPixel() &&
dstIt.nextPixel() &&
indexFetcher.nextPixel());
}
}
void applyGradient(KisPaintDeviceSP device,
KisPixelSelectionSP selection,
const QRect &applyRect,
const QVector<KoColor> &table,
bool edgeHidden,
int jitter)
{
if (!jitter) {
LinearGradientIndex fetcher;
applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher);
} else {
JitterGradientIndex fetcher(applyRect, jitter);
applyGradientImpl(device, selection, applyRect, table, edgeHidden, fetcher);
}
}
}
const int noiseNeedBorder = 8;
void applyNoise(KisPixelSelectionSP selection,
const QRect &applyRect,
int noise,
const psd_layer_effects_context *context)
{
Q_UNUSED(context);
const QRect overlayRect = kisGrowRect(applyRect, noiseNeedBorder);
KisPixelSelectionSP randomSelection = Private::generateRandomSelection(overlayRect);
KisPixelSelectionSP randomOverlay = new KisPixelSelection();
KisSequentialConstIterator noiseIt(randomSelection, overlayRect);
KisSequentialConstIterator srcIt(selection, overlayRect);
KisRandomAccessorSP dstIt = randomOverlay->createRandomAccessorNG(overlayRect.x(), overlayRect.y());
do {
int itX = noiseIt.x();
int itY = noiseIt.y();
int x = itX + (*noiseIt.rawDataConst() >> 4) - 8;
int y = itY + (*noiseIt.rawDataConst() & 0x0F) - 8;
x = (x + itX) >> 1;
y = (y + itY) >> 1;
dstIt->moveTo(x, y);
quint8 dstAlpha = *dstIt->rawData();
quint8 srcAlpha = *srcIt.rawDataConst();
int value = qMin(255, dstAlpha + srcAlpha);
*dstIt->rawData() = value;
} while(noiseIt.nextPixel() && srcIt.nextPixel());
noise = noise * 255 / 100;
KisPainter gc(selection);
gc.setOpacity(noise);
gc.setCompositeOp(COMPOSITE_COPY);
gc.bitBlt(applyRect.topLeft(), randomOverlay, applyRect);
}
//const int FULL_PERCENT_RANGE = 100;
void adjustRange(KisPixelSelectionSP selection, const QRect &applyRect, const int range)
{
KIS_ASSERT_RECOVER_RETURN(range >= 1 && range <= 100);
quint8 rangeTable[256];
for(int i = 0; i < 256; i ++) {
quint8 value = i * 100 / range;
rangeTable[i] = qMin(value, quint8(255));
}
KisSequentialIterator dstIt(selection, applyRect);
do {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = rangeTable[*pixelPtr];
} while(dstIt.nextPixel());
}
void applyContourCorrection(KisPixelSelectionSP selection,
const QRect &applyRect,
const quint8 *lookup_table,
bool antiAliased,
bool edgeHidden)
{
quint8 contour[PSD_LOOKUP_TABLE_SIZE] = {
0x00, 0x0b, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d, 0x58, 0x63, 0x6e, 0x79, 0x84, 0x8f, 0x9a, 0xa5,
0xb0, 0xbb, 0xc6, 0xd1, 0xdc, 0xf2, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if (edgeHidden) {
if (antiAliased) {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = contour[i] * lookup_table[i] >> 8;
}
} else {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = contour[i] * lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)] >> 8;
}
}
} else {
if (antiAliased) {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = lookup_table[i];
}
} else {
for (int i = 0; i < PSD_LOOKUP_TABLE_SIZE; i++) {
contour[i] = lookup_table[(int)((int)(i / 2.55) * 2.55 + 0.5)];
}
}
}
KisSequentialIterator dstIt(selection, applyRect);
do {
quint8 *pixelPtr = dstIt.rawData();
*pixelPtr = contour[*pixelPtr];
} while(dstIt.nextPixel());
}
void knockOutSelection(KisPixelSelectionSP selection,
KisPixelSelectionSP knockOutSelection,
const QRect &srcRect,
const QRect &dstRect,
const QRect &totalNeedRect,
const bool knockOutInverted)
{
KIS_ASSERT_RECOVER_RETURN(knockOutSelection);
QRect knockOutRect = !knockOutInverted ? srcRect : totalNeedRect;
knockOutRect &= dstRect;
KisPainter gc(selection);
gc.setCompositeOp(COMPOSITE_ERASE);
gc.bitBlt(knockOutRect.topLeft(), knockOutSelection, knockOutRect);
}
void fillPattern(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
KisLayerStyleFilterEnvironment *env,
int scale,
KoPattern *pattern,
int horizontalPhase,
int verticalPhase,
bool alignWithLayer)
{
if (scale != 100) {
- qWarning() << "KisLsOverlayFilter::applyOverlay(): Pattern scaling is NOT implemented!";
+ warnKrita << "KisLsOverlayFilter::applyOverlay(): Pattern scaling is NOT implemented!";
}
QSize psize(pattern->width(), pattern->height());
QPoint patternOffset(qreal(psize.width()) * horizontalPhase / 100,
qreal(psize.height()) * verticalPhase / 100);
const QRect boundsRect = alignWithLayer ?
env->layerBounds() : env->defaultBounds();
patternOffset += boundsRect.topLeft();
patternOffset.rx() %= psize.width();
patternOffset.ry() %= psize.height();
QRect fillRect = applyRect | applyRect.translated(patternOffset);
KisFillPainter gc(fillDevice);
gc.fillRect(fillRect.x(), fillRect.y(),
fillRect.width(), fillRect.height(), pattern);
gc.end();
fillDevice->setX(-patternOffset.x());
fillDevice->setY(-patternOffset.y());
}
void fillOverlayDevice(KisPaintDeviceSP fillDevice,
const QRect &applyRect,
const psd_layer_effects_overlay_base *config,
KisLayerStyleFilterEnvironment *env)
{
if (config->fillType() == psd_fill_solid_color) {
KoColor color(config->color(), fillDevice->colorSpace());
fillDevice->setDefaultPixel(color.data());
} else if (config->fillType() == psd_fill_pattern) {
fillPattern(fillDevice, applyRect, env,
config->scale(), config->pattern(),
config->horizontalPhase(),
config->verticalPhase(),
config->alignWithLayer());
} else if (config->fillType() == psd_fill_gradient) {
const QRect boundsRect = config->alignWithLayer() ?
env->layerBounds() : env->defaultBounds();
QPoint center = boundsRect.center();
center += QPoint(boundsRect.width() * config->gradientXOffset() / 100,
boundsRect.height() * config->gradientYOffset() / 100);
int width = (boundsRect.width() * config->scale() + 100) / 200;
int height = (boundsRect.height() * config->scale() + 100) / 200;
/* copy paste from libpsd */
int angle = config->angle();
int corner_angle = (int)(atan((qreal)boundsRect.height() / boundsRect.width()) * 180 / M_PI + 0.5);
int sign_x = 1;
int sign_y = 1;
if(angle < 0) {
angle += 360;
}
if (angle >= 90 && angle < 180) {
angle = 180 - angle;
sign_x = -1;
} else if (angle >= 180 && angle < 270) {
angle = angle - 180;
sign_x = -1;
sign_y = -1;
} else if (angle >= 270 && angle <= 360) {
angle = 360 - angle;
sign_y = -1;
}
int radius_x = 0;
int radius_y = 0;
if (angle <= corner_angle) {
radius_x = width;
radius_y = (int)(radius_x * tan(kisDegreesToRadians(qreal(angle))) + 0.5);
} else {
radius_y = height;
radius_x = (int)(radius_y / tan(kisDegreesToRadians(qreal(angle))) + 0.5);
}
int radius_corner = (int)(std::sqrt((qreal)(radius_x * radius_x + radius_y * radius_y)) + 0.5);
/* end of copy paste from libpsd */
KisGradientPainter gc(fillDevice);
gc.setGradient(config->gradient().data());
QPointF gradStart;
QPointF gradEnd;
KisGradientPainter::enumGradientRepeat repeat =
KisGradientPainter::GradientRepeatNone;
QPoint rectangularOffset(sign_x * radius_x, -sign_y * radius_y);
switch(config->style())
{
case psd_gradient_style_linear:
gc.setGradientShape(KisGradientPainter::GradientShapeLinear);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center - rectangularOffset;
gradEnd = center + rectangularOffset;
break;
case psd_gradient_style_radial:
gc.setGradientShape(KisGradientPainter::GradientShapeRadial);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center;
gradEnd = center + QPointF(radius_corner, 0);
break;
case psd_gradient_style_angle:
gc.setGradientShape(KisGradientPainter::GradientShapeConical);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center;
gradEnd = center + rectangularOffset;
break;
case psd_gradient_style_reflected:
gc.setGradientShape(KisGradientPainter::GradientShapeLinear);
repeat = KisGradientPainter::GradientRepeatAlternate;
gradStart = center - rectangularOffset;
gradEnd = center;
break;
case psd_gradient_style_diamond:
gc.setGradientShape(KisGradientPainter::GradientShapeBiLinear);
repeat = KisGradientPainter::GradientRepeatNone;
gradStart = center - rectangularOffset;
gradEnd = center + rectangularOffset;
break;
default:
qFatal("Gradient Overlay: unknown switch case!");
break;
}
gc.paintGradient(gradStart, gradEnd,
repeat, 0.0,
config->reverse(),
applyRect);
}
}
void applyFinalSelection(KisSelectionSP baseSelection,
KisPaintDeviceSP srcDevice,
KisPaintDeviceSP dstDevice,
const QRect &srcRect,
const QRect &dstRect,
const psd_layer_effects_context *context,
const psd_layer_effects_shadow_base *config,
const KisLayerStyleFilterEnvironment *env)
{
const KoColor effectColor(config->color(), srcDevice->colorSpace());
KisPaintDeviceSP tempDevice;
if (srcDevice == dstDevice) {
if (context->keep_original) {
tempDevice = new KisPaintDevice(*srcDevice);
}
srcDevice->clear(srcRect);
} else {
tempDevice = srcDevice;
}
const QRect effectRect(dstRect);
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = 255.0 / 100.0 * config->opacity();
if (config->fillType() == psd_fill_solid_color) {
KisFillPainter gc(dstDevice);
gc.setCompositeOp(compositeOp);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.setSelection(baseSelection);
gc.fillSelection(effectRect, effectColor);
gc.end();
} else if (config->fillType() == psd_fill_gradient) {
if (!config->gradient()) {
- qWarning() << "KisLsUtils::applyFinalSelection: Gradient object is null! Skipping...";
+ warnKrita << "KisLsUtils::applyFinalSelection: Gradient object is null! Skipping...";
return;
}
KisPaintDeviceSP overlayDevice =
new KisPaintDevice(dstDevice->colorSpace());
QVector<KoColor> table(256);
Private::getGradientTable(config->gradient().data(), &table, dstDevice->colorSpace());
Private::applyGradient(overlayDevice, baseSelection->pixelSelection(),
effectRect, table,
true, config->jitter());
KisPainter gc(dstDevice);
gc.setCompositeOp(compositeOp);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.bitBlt(effectRect.topLeft(), overlayDevice, effectRect);
gc.end();
}
//dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("6_device_shadow.png");
if (context->keep_original) {
KisPainter gc(dstDevice);
// FIXME: opacity?
gc.bitBlt(dstRect.topLeft(), tempDevice, dstRect);
}
}
}
diff --git a/krita/image/metadata/kis_meta_data_entry.h b/krita/image/metadata/kis_meta_data_entry.h
index 178a5f9743c..937289c3dbb 100644
--- a/krita/image/metadata/kis_meta_data_entry.h
+++ b/krita/image/metadata/kis_meta_data_entry.h
@@ -1,97 +1,97 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_META_DATA_ENTRY_H_
#define _KIS_META_DATA_ENTRY_H_
#include <kritaimage_export.h>
-#include <QDebug>
+#include <kis_debug.h>
class QString;
namespace KisMetaData
{
class Value;
class Store;
class Schema;
/**
* Represent a metadata entry, a name and a value (\ref KisMetaData::Value).
*/
class KRITAIMAGE_EXPORT Entry
{
struct Private;
friend class Store;
public:
/**
* Create an invalid entry
*/
Entry();
/**
* Create a new entry.
* @param name
* @param namespacePrefix
* @param value
*/
Entry(const KisMetaData::Schema* schema, QString name, const KisMetaData::Value& value);
Entry(const Entry&);
~Entry();
/**
* @return the name of this entry
*/
QString name() const;
/**
* @return the namespace of this entry
*/
const KisMetaData::Schema* schema() const;
/**
* @return the qualified name of this entry, which is the concatenation of the
* namespace and of the name
*/
QString qualifiedName() const;
/**
* @return the value of this entry
*/
const KisMetaData::Value& value() const;
/**
* @return the value of this entry
*/
KisMetaData::Value& value();
/**
* @return true if this entry is valid
*/
bool isValid() const;
/**
* @return true if the name in argument is valid entry name.
*/
static bool isValidName(const QString& _name);
/**
* Affect the content of entry to this entry if entry is valid
*/
Entry& operator=(const Entry& entry);
bool operator==(const Entry&) const;
private:
void setSchema(const KisMetaData::Schema* schema);
private:
Private* const d;
};
}
KRITAIMAGE_EXPORT QDebug operator<<(QDebug debug, const KisMetaData::Entry &c);
#endif
diff --git a/krita/image/metadata/kis_meta_data_schema.cc b/krita/image/metadata/kis_meta_data_schema.cc
index 87690f998c9..121216ec037 100644
--- a/krita/image/metadata/kis_meta_data_schema.cc
+++ b/krita/image/metadata/kis_meta_data_schema.cc
@@ -1,387 +1,387 @@
/*
* Copyright (c) 2007,2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_meta_data_schema.h"
#include <QDateTime>
#include <QDomDocument>
#include <QFile>
#include <QString>
#include <QVariant>
#include "kis_debug.h"
#include "kis_meta_data_type_info_p.h"
#include "kis_meta_data_schema_p.h"
#include "kis_meta_data_value.h"
using namespace KisMetaData;
const QString Schema::TIFFSchemaUri = "http://ns.adobe.com/tiff/1.0/";
const QString Schema::EXIFSchemaUri = "http://ns.adobe.com/exif/1.0/";
const QString Schema::DublinCoreSchemaUri = "http://purl.org/dc/elements/1.1/";
const QString Schema::XMPSchemaUri = "http://ns.adobe.com/xap/1.0/";
const QString Schema::XMPRightsSchemaUri = "http://ns.adobe.com/xap/1.0/rights/";
const QString Schema::XMPMediaManagementUri = "http://ns.adobe.com/xap/1.0/sType/ResourceRef#";
const QString Schema::MakerNoteSchemaUri = "http://www.calligra.org/krita/xmp/MakerNote/1.0/";
const QString Schema::IPTCSchemaUri = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/";
const QString Schema::PhotoshopSchemaUri = "http://ns.adobe.com/photoshop/1.0/";
bool Schema::Private::load(const QString& _fileName)
{
dbgImage << "Loading from " << _fileName;
QDomDocument document;
QString error;
int ligne, column;
QFile file(_fileName);
if (document.setContent(&file, &error, &ligne, &column)) {
QDomElement docElem = document.documentElement();
if (docElem.tagName() != "schema") {
dbgImage << _fileName << ": invalid root name";
return false;
}
if (!docElem.hasAttribute("prefix")) {
dbgImage << _fileName << ": missing prefix.";
return false;
}
if (!docElem.hasAttribute("uri")) {
dbgImage << _fileName << ": missing uri.";
return false;
}
prefix = docElem.attribute("prefix");
uri = docElem.attribute("uri");
dbgImage << ppVar(prefix) << ppVar(uri);
QDomNode n = docElem.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
if (e.tagName() == "structures") {
parseStructures(e);
} else if (e.tagName() == "properties") {
parseProperties(e);
}
}
n = n.nextSibling();
}
return true;
} else {
dbgImage << error << " at " << ligne << ", " << column << " in " << _fileName;
return false;
}
}
void Schema::Private::parseStructures(QDomElement& elt)
{
Q_ASSERT(elt.tagName() == "structures");
dbgImage << "Parse sturctures";
QDomNode n = elt.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
if (e.tagName() == "structure") {
parseStructure(e);
} else {
errImage << "Invalid tag: " << e.tagName() << " in structures section";
}
}
n = n.nextSibling();
}
}
void Schema::Private::parseStructure(QDomElement& elt)
{
Q_ASSERT(elt.tagName() == "structure");
if (!elt.hasAttribute("name")) {
errImage << "Name is required for a structure";
return;
}
QString structureName = elt.attribute("name");
if (structures.contains(structureName)) {
errImage << structureName << " is defined twice";
return;
}
dbgImage << "Parsing structure " << structureName;
if (!elt.hasAttribute("prefix")) {
errImage << "prefix is required for structure " << structureName;
return;
}
if (!elt.hasAttribute("uri")) {
errImage << "uri is required for structure " << structureName;
return;
}
QString structurePrefix = elt.attribute("prefix");
QString structureUri = elt.attribute("uri");
dbgImage << ppVar(structurePrefix) << ppVar(structureUri);
Schema* schema = new Schema(structureUri, structurePrefix);
QDomNode n = elt.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
EntryInfo info;
QString name;
if (parseEltType(e, info, name, false, false)) {
if (schema->d->types.contains(name)) {
errImage << structureName << " already contains a field " << name;
} else {
schema->d->types[ name ] = info;
}
}
}
n = n.nextSibling();
}
structures[ structureName ] = TypeInfo::Private::createStructure(schema, structureName);
}
void Schema::Private::parseProperties(QDomElement& elt)
{
Q_ASSERT(elt.tagName() == "properties");
dbgImage << "Parse properties";
QDomNode n = elt.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
EntryInfo info;
QString name;
if (parseEltType(e, info, name, false, false)) {
if (types.contains(name)) {
errImage << name << " already defined.";
} else {
types[ name ] = info;
}
}
}
n = n.nextSibling();
}
}
bool Schema::Private::parseEltType(QDomElement& elt, EntryInfo& entryInfo, QString& name, bool ignoreStructure, bool ignoreName)
{
QString tagName = elt.tagName();
if (!ignoreName && !elt.hasAttribute("name")) {
errImage << "Missing name attribute for tag " << tagName;
return false;
}
name = elt.attribute("name");
// TODO parse qualifier
if (tagName == "integer") {
entryInfo.propertyType = TypeInfo::Private::Integer;
return true;
} else if (tagName == "boolean") {
entryInfo.propertyType = TypeInfo::Private::Boolean;
return true;
} else if (tagName == "date") {
entryInfo.propertyType = TypeInfo::Private::Date;
return true;
} else if (tagName == "text") {
entryInfo.propertyType = TypeInfo::Private::Text;
return true;
} else if (tagName == "seq") {
const TypeInfo* ei = parseAttType(elt, ignoreStructure);
if (!ei) {
ei = parseEmbType(elt, ignoreStructure);
}
if (!ei) {
errImage << "No type defined for " << name;
return false;
}
entryInfo.propertyType = TypeInfo::Private::orderedArray(ei);
return true;
} else if (tagName == "bag") {
const TypeInfo* ei = parseAttType(elt, ignoreStructure);
if (!ei) {
ei = parseEmbType(elt, ignoreStructure);
}
if (!ei) {
errImage << "No type defined for " << name;
return false;
}
entryInfo.propertyType = TypeInfo::Private::unorderedArray(ei);
return true;
} else if (tagName == "alt") {
const TypeInfo* ei = parseAttType(elt, ignoreStructure);
if (!ei) {
ei = parseEmbType(elt, ignoreStructure);
}
if (!ei) {
errImage << "No type defined for " << name;
return false;
}
entryInfo.propertyType = TypeInfo::Private::alternativeArray(ei);
return true;
} else if (tagName == "lang") {
entryInfo.propertyType = TypeInfo::Private::LangArray;
return true;
} else if (tagName == "rational") {
entryInfo.propertyType = TypeInfo::Private::Rational;
return true;
} else if (tagName == "gpscoordinate") {
entryInfo.propertyType = TypeInfo::Private::GPSCoordinate;
return true;
} else if (tagName == "openedchoice" || tagName == "closedchoice") {
entryInfo.propertyType = parseChoice(elt);
return true;
} else if (!ignoreStructure && structures.contains(tagName)) {
entryInfo.propertyType = structures.value(tagName);
return true;
}
errImage << tagName << " isn't a type.";
return false;
}
const TypeInfo* Schema::Private::parseAttType(QDomElement& elt, bool ignoreStructure)
{
if (!elt.hasAttribute("type")) {
return 0;
}
QString type = elt.attribute("type");
if (type == "integer") {
return TypeInfo::Private::Integer;
} else if (type == "boolean") {
return TypeInfo::Private::Boolean;
} else if (type == "date") {
return TypeInfo::Private::Date;
} else if (type == "text") {
return TypeInfo::Private::Text;
} else if (type == "rational") {
return TypeInfo::Private::Rational;
} else if (!ignoreStructure && structures.contains(type)) {
return structures[type];
}
errImage << "Unsupported type: " << type << " in an attribute";
return 0;
}
const TypeInfo* Schema::Private::parseEmbType(QDomElement& elt, bool ignoreStructure)
{
dbgImage << "Parse embbedded type for " << elt.tagName();
QDomNode n = elt.firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
QString type = e.tagName();
if (type == "integer") {
return TypeInfo::Private::Integer;
} else if (type == "boolean") {
return TypeInfo::Private::Boolean;
} else if (type == "date") {
return TypeInfo::Private::Date;
} else if (type == "text") {
return TypeInfo::Private::Text;
} else if (type == "openedchoice" || type == "closedchoice") {
return parseChoice(e);
} else if (!ignoreStructure && structures.contains(type)) {
return structures[type];
}
}
n = n.nextSibling();
}
return 0;
}
const TypeInfo* Schema::Private::parseChoice(QDomElement& elt)
{
const TypeInfo* choiceType = parseAttType(elt, true);
TypeInfo::PropertyType propertyType;
if (elt.tagName() == "openedchoice") {
propertyType = TypeInfo::OpenedChoice;
} else {
Q_ASSERT(elt.tagName() == "closedchoice");
propertyType = TypeInfo::ClosedChoice;
}
QDomNode n = elt.firstChild();
QList< TypeInfo::Choice > choices;
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
EntryInfo info;
QString name;
if (parseEltType(e, info, name, true, true)) {
if (! choiceType) choiceType = info.propertyType;
if (choiceType == info.propertyType) {
QString text = e.text();
QVariant var = text;
if (choiceType->propertyType() == TypeInfo::IntegerType) {
var = var.toInt();
} else if (choiceType->propertyType() == TypeInfo::DateType) { // TODO QVariant date parser isn't very good with XMP date (it doesn't support YYYY and YYYY-MM
var = var.toDateTime();
}
choices.push_back(TypeInfo::Choice(Value(var), name));
} else {
errImage << "All members of a choice need to be of the same type";
}
}
}
n = n.nextSibling();
}
return TypeInfo::Private::createChoice(propertyType, choiceType, choices);
}
Schema::Schema()
: d(new Private)
{
}
Schema::Schema(const QString & _uri, const QString & _ns)
: d(new Private)
{
d->uri = _uri;
d->prefix = _ns;
}
Schema::~Schema()
{
dbgImage << "Deleting schema " << d->uri << " " << d->prefix;
- dbgImage << kBacktrace();
+ dbgImage << kisBacktrace();
delete d;
}
const TypeInfo* Schema::propertyType(const QString& _propertyName) const
{
if (d->types.contains(_propertyName)) {
return d->types.value(_propertyName).propertyType;
}
return 0;
}
const TypeInfo* Schema::structure(const QString& _structureName) const
{
return d->structures.value(_structureName);
}
QString Schema::uri() const
{
return d->uri;
}
QString Schema::prefix() const
{
return d->prefix;
}
QString Schema::generateQualifiedName(const QString & name) const
{
dbgImage << "generateQualifiedName for " << name;
Q_ASSERT(!name.isEmpty() && !name.isNull());
return prefix() + ':' + name;
}
QDebug operator<<(QDebug debug, const KisMetaData::Schema &c)
{
debug.nospace() << "Uri = " << c.uri() << " Prefix = " << c.prefix();
return debug.space();
}
diff --git a/krita/image/metadata/kis_meta_data_schema.h b/krita/image/metadata/kis_meta_data_schema.h
index 1f0431eea45..cd1f46508f2 100644
--- a/krita/image/metadata/kis_meta_data_schema.h
+++ b/krita/image/metadata/kis_meta_data_schema.h
@@ -1,75 +1,75 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_META_DATA_SCHEMA_H_
#define _KIS_META_DATA_SCHEMA_H_
#include <kritaimage_export.h>
-#include <QDebug>
+#include <kis_debug.h>
class QString;
namespace KisMetaData
{
class SchemaRegistry;
class TypeInfo;
class KRITAIMAGE_EXPORT Schema
{
friend class SchemaRegistry;
public:
virtual ~Schema();
static const QString TIFFSchemaUri;
static const QString EXIFSchemaUri;
static const QString DublinCoreSchemaUri;
static const QString XMPSchemaUri;
static const QString XMPRightsSchemaUri;
static const QString XMPMediaManagementUri;
static const QString MakerNoteSchemaUri;
static const QString IPTCSchemaUri;
static const QString PhotoshopSchemaUri;
private:
Schema();
Schema(const QString & _uri, const QString & _ns);
public:
/**
* @return the \ref TypeInfo associated with a given a property ( @p _propertyName ).
*/
const TypeInfo* propertyType(const QString& _propertyName) const;
/**
* @return the \ref TypeInfo describing a given structure of that scheam
*/
const TypeInfo* structure(const QString& _structureName) const;
public:
QString uri() const;
QString prefix() const;
QString generateQualifiedName(const QString &) const;
private:
struct Private;
Private* const d;
};
}
KRITAIMAGE_EXPORT QDebug operator<<(QDebug debug, const KisMetaData::Schema &c);
#endif
diff --git a/krita/image/processing/kis_transform_processing_visitor.cpp b/krita/image/processing/kis_transform_processing_visitor.cpp
index 134eb8b0fce..77c0eb433ee 100644
--- a/krita/image/processing/kis_transform_processing_visitor.cpp
+++ b/krita/image/processing/kis_transform_processing_visitor.cpp
@@ -1,210 +1,210 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transform_processing_visitor.h"
#include "klocale.h"
#include <KoUpdater.h>
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "generator/kis_generator_layer.h"
#include "kis_transparency_mask.h"
#include "kis_filter_mask.h"
#include "kis_transform_mask.h"
#include "kis_selection_mask.h"
#include "kis_external_layer_iface.h"
#include "kis_transaction.h"
#include "kis_undo_adapter.h"
#include "kis_transform_worker.h"
#include "commands_new/kis_node_move_command2.h"
#include "kis_do_something_command.h"
KisTransformProcessingVisitor::
KisTransformProcessingVisitor(qreal xscale, qreal yscale,
qreal xshear, qreal yshear,
const QPointF &shearOrigin,
qreal angle,
qint32 tx, qint32 ty,
KisFilterStrategy *filter,
const QTransform &shapesCorrection)
: m_sx(xscale), m_sy(yscale)
, m_tx(tx), m_ty(ty)
, m_shearx(xshear), m_sheary(yshear)
, m_shearOrigin(shearOrigin)
, m_filter(filter)
, m_angle(angle)
, m_shapesCorrection(shapesCorrection)
{
}
void KisTransformProcessingVisitor::visit(KisNode *node, KisUndoAdapter *undoAdapter)
{
Q_UNUSED(node);
Q_UNUSED(undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisPaintLayer *layer, KisUndoAdapter *undoAdapter)
{
transformPaintDevice(layer->paintDevice(), undoAdapter, ProgressHelper(layer));
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisGroupLayer *layer, KisUndoAdapter *undoAdapter)
{
using namespace KisDoSomethingCommandOps;
undoAdapter->addCommand(new KisDoSomethingCommand<ResetOp, KisGroupLayer*>(layer, false));
undoAdapter->addCommand(new KisDoSomethingCommand<ResetOp, KisGroupLayer*>(layer, true));
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisAdjustmentLayer *layer, KisUndoAdapter *undoAdapter)
{
using namespace KisDoSomethingCommandOps;
undoAdapter->addCommand(new KisDoSomethingCommand<ResetOp, KisAdjustmentLayer*>(layer, false));
transformSelection(layer->internalSelection(), undoAdapter, ProgressHelper(layer));
undoAdapter->addCommand(new KisDoSomethingCommand<ResetOp, KisAdjustmentLayer*>(layer, true));
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisExternalLayer *layer, KisUndoAdapter *undoAdapter)
{
KisTransformWorker tw(layer->projection(), m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, 0,
m_filter);
KUndo2Command* command = layer->transform(tw.transform() * m_shapesCorrection);
if(command) {
undoAdapter->addCommand(command);
}
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisGeneratorLayer *layer, KisUndoAdapter *undoAdapter)
{
using namespace KisDoSomethingCommandOps;
undoAdapter->addCommand(new KisDoSomethingCommand<UpdateOp, KisGeneratorLayer*>(layer, false));
transformSelection(layer->internalSelection(), undoAdapter, ProgressHelper(layer));
undoAdapter->addCommand(new KisDoSomethingCommand<UpdateOp, KisGeneratorLayer*>(layer, true));
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisCloneLayer *layer, KisUndoAdapter *undoAdapter)
{
transformClones(layer, undoAdapter);
}
void KisTransformProcessingVisitor::visit(KisFilterMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter, ProgressHelper(mask));
}
void KisTransformProcessingVisitor::visit(KisTransformMask *mask, KisUndoAdapter *undoAdapter)
{
Q_UNUSED(mask);
Q_UNUSED(undoAdapter);
- qWarning() << "WARNING: transformation of the transform mask is not implemented";
+ warnKrita << "WARNING: transformation of the transform mask is not implemented";
}
void KisTransformProcessingVisitor::visit(KisTransparencyMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter, ProgressHelper(mask));
}
void KisTransformProcessingVisitor::visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter)
{
transformSelection(mask->selection(), undoAdapter, ProgressHelper(mask));
}
void KisTransformProcessingVisitor::transformClones(KisLayer *layer, KisUndoAdapter *undoAdapter)
{
QList<KisCloneLayerWSP> clones = layer->registeredClones();
foreach(KisCloneLayerSP clone, clones) {
// we have just casted an object from a weak pointer,
// so check validity first
if(!clone) continue;
KisTransformWorker tw(clone->projection(), m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, 0,
m_filter);
QTransform trans = tw.transform();
QTransform offsetTrans = QTransform::fromTranslate(clone->x(), clone->y());
QTransform newTrans = trans.inverted() * offsetTrans * trans;
QPoint oldPos(clone->x(), clone->y());
QPoint newPos(newTrans.dx(), newTrans.dy());
KUndo2Command *command = new KisNodeMoveCommand2(clone, oldPos, newPos);
undoAdapter->addCommand(command);
}
}
void KisTransformProcessingVisitor::transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *adapter, const ProgressHelper &helper)
{
KisTransaction transaction(kundo2_i18n("Transform Layer"), device);
KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, helper.updater(),
m_filter);
tw.run();
transaction.commit(adapter);
}
void KisTransformProcessingVisitor::transformSelection(KisSelectionSP selection, KisUndoAdapter *adapter, const ProgressHelper &helper)
{
if(selection->hasPixelSelection()) {
transformPaintDevice(selection->pixelSelection(), adapter, helper);
}
if (selection->hasShapeSelection()) {
KisTransformWorker tw(selection->projection(), m_sx, m_sy, m_shearx, m_sheary,
m_shearOrigin.x(), m_shearOrigin.y(),
m_angle, m_tx, m_ty, 0,
m_filter);
KUndo2Command* command =
selection->shapeSelection()->transform(tw.transform() * m_shapesCorrection);
if (command) {
adapter->addCommand(command);
}
}
selection->updateProjection();
}
diff --git a/krita/image/recorder/kis_node_query_path.cc b/krita/image/recorder/kis_node_query_path.cc
index 837a4076bab..9071b550f77 100644
--- a/krita/image/recorder/kis_node_query_path.cc
+++ b/krita/image/recorder/kis_node_query_path.cc
@@ -1,217 +1,217 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_node_query_path.h"
#include <QStringList>
#include <kis_node.h>
#include <kis_image.h>
#include <kis_paint_device.h>
struct PathElement {
enum Type {
Wildcard,
Parent,
Index
};
PathElement(Type _type) : type(_type) {
Q_ASSERT(type == Wildcard || type == Parent);
}
PathElement(int _i) : type(Index), index(_i) {}
Type type;
unsigned int index;
};
struct Q_DECL_HIDDEN KisNodeQueryPath::Private {
QList<PathElement> elements;
bool relative;
/// This function will remove uneeded call to parent, for instance, "1/../3/../5" => "5"
void simplifyPath() {
// No elements then return
if (elements.isEmpty()) return;
QList<PathElement> newelements;
int i = 0;
for (; i < elements.count() && elements[i].type == PathElement::Parent; ++i) {
newelements.push_back(PathElement::Parent);
}
// Loop ofver the element of the list
for (; i < elements.count(); ++i) {
PathElement pe = elements[i];
// If it's the last element, or the next element isn't a parent
if (pe.type != PathElement::Parent) {
newelements.push_back(pe);
} else {
if (newelements.isEmpty() || newelements.last().type == PathElement::Parent) {
newelements.push_back(PathElement::Parent);
} else {
newelements.removeLast();
}
}
}
// Set the new list
elements = newelements;
}
void queryLevel(int _level, KisNodeSP _node, QList<KisNodeSP>& _result) {
if (_level >= elements.size()) {
_result.push_back(_node);
} else {
PathElement pe = elements[_level];
switch (pe.type) {
case PathElement::Wildcard: {
for (KisNodeSP child = _node->firstChild();
child != 0; child = child->nextSibling()) {
queryLevel(_level + 1, child, _result);
}
}
break;
case PathElement::Parent: {
if (_node->parent()) {
queryLevel(_level + 1, _node->parent(), _result);
} else {
- kWarning() << "No parent";
+ dbgKrita << "No parent";
}
break;
}
case PathElement::Index: {
if (pe.index < _node->childCount()) {
queryLevel(_level + 1, _node->at(pe.index), _result);
} else {
- kWarning() << "No parent";
+ dbgKrita << "No parent";
}
break;
}
}
}
}
};
KisNodeQueryPath::KisNodeQueryPath() : d(new Private)
{
}
KisNodeQueryPath::~KisNodeQueryPath()
{
delete d;
}
KisNodeQueryPath::KisNodeQueryPath(const KisNodeQueryPath& nqp) : d(new Private(*nqp.d))
{
}
KisNodeQueryPath& KisNodeQueryPath::operator=(const KisNodeQueryPath & nqp)
{
*d = *nqp.d;
return *this;
}
bool KisNodeQueryPath::isRelative() const
{
return d->relative;
}
QList<KisNodeSP> KisNodeQueryPath::queryNodes(KisImageWSP image, KisNodeSP currentNode) const
{
KisNodeSP _node;
if (d->relative) {
_node = currentNode;
} else {
_node = image->root();
}
QList<KisNodeSP> result;
d->queryLevel(0, _node, result);
return result;
}
QString KisNodeQueryPath::toString() const
{
QString str;
if (!d->relative) {
str = '/';
} else if (d->elements.count() == 0) {
return QString('.');
}
for (int i = 0; i < d->elements.count(); ++i) {
PathElement pe = d->elements[i];
switch (pe.type) {
case PathElement::Wildcard:
str += '*';
break;
case PathElement::Parent:
str += "..";
break;
case PathElement::Index:
str += QString::number(pe.index);
break;
}
if (i != d->elements.count() - 1) {
str += '/';
}
}
return str;
}
KisNodeQueryPath KisNodeQueryPath::fromString(const QString& _path)
{
KisNodeQueryPath path;
if (_path.size() == 0 || _path == ".") {
path.d->relative = true;
return path;
}
if (_path == "/") {
path.d->relative = false;
return path;
}
path.d->relative = !(_path.at(0) == '/');
QStringList indexes = _path.split('/');
if (!path.d->relative) {
indexes.pop_front(); // In case of an absolute path "/1/2", the list is "", "1", "2" which is not good
}
foreach(const QString& index, indexes) {
if (index == "*") {
path.d->elements.push_back(PathElement::Wildcard);
} else if (index == "..") {
path.d->elements.push_back(PathElement::Parent);
} else {
path.d->elements.push_back(index.toInt());
}
}
path.d->simplifyPath();
return path;
}
KisNodeQueryPath KisNodeQueryPath::absolutePath(KisNodeSP node)
{
KisNodeQueryPath path;
path.d->relative = false;
KisNodeSP parent = 0;
while ((parent = node->parent())) {
int index = parent->index(node);
if (index >= 0) {
path.d->elements.push_front(index);
}
node = parent;
}
return path;
}
diff --git a/krita/image/tests/kis_async_merger_test.cpp b/krita/image/tests/kis_async_merger_test.cpp
index 9260d9a8575..55f1e626ff6 100644
--- a/krita/image/tests/kis_async_merger_test.cpp
+++ b/krita/image/tests/kis_async_merger_test.cpp
@@ -1,291 +1,291 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_async_merger_test.h"
#include "kis_merge_walker.h"
#include "kis_full_refresh_walker.h"
#include "kis_async_merger.h"
#include <qtest_kde.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_filter_mask.h"
#include "kis_selection.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "../../sdk/tests/testutil.h"
/*
+-----------+
|root |
| group |
| blur 1 |
| paint 2 |
| paint 1 |
+-----------+
*/
void KisAsyncMergerTest::testMerger()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage sourceImage2(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
QImage referenceProjection(QString(FILES_DATA_DIR) + QDir::separator() + "merged_hakonepa.png");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
device1->convertFromQImage(sourceImage1, 0, 0, 0);
device2->convertFromQImage(sourceImage2, 0, 0, 0);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration = filter->defaultConfiguration(0);
Q_ASSERT(configuration);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", 200/*OPACITY_OPAQUE*/);
KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(blur1, groupLayer);
QRect testRect1(0,0,100,441);
QRect testRect2(100,0,400,441);
QRect testRect3(500,0,140,441);
QRect testRect4(580,381,40,40);
QRect cropRect(image->bounds());
KisMergeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer2, testRect1);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect2);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect3);
merger.startMerge(walker);
walker.collectRects(paintLayer2, testRect4);
merger.startMerge(walker);
// Old style merging: has artefacts at x=100 and x=500
// And should be turned on inside KisLayer
/* paintLayer2->setDirty(testRect1);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect2);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect3);
QTest::qSleep(3000);
paintLayer2->setDirty(testRect4);
QTest::qSleep(3000);
*/
KisLayerSP rootLayer = image->rootLayer();
QVERIFY(rootLayer->exactBounds() == image->bounds());
QImage resultProjection = rootLayer->projection()->convertToQImage(0);
resultProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "actual_merge_result.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, resultProjection, referenceProjection, 5, 0, 0));
}
/**
* This in not fully automated test for child obliging in KisAsyncMerger.
* It just checks whether devices are shared. To check if the merger
* touches originals you can add a debug message to the merger
* and take a look.
*/
/*
+-----------+
|root |
| group |
| paint 1 |
+-----------+
*/
void KisAsyncMergerTest::debugObligeChild()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->convertFromQImage(sourceImage1, 0, 0, 0);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer1, groupLayer);
QRect testRect1(0,0,640,441);
QRect cropRect(image->bounds());
KisMergeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer1, testRect1);
merger.startMerge(walker);
KisLayerSP rootLayer = image->rootLayer();
QVERIFY(rootLayer->original() == groupLayer->projection());
QVERIFY(groupLayer->original() == paintLayer1->projection());
}
/*
+--------------+
|root |
| paint 1 |
| invert_mask |
| clone_of_1 |
+--------------+
*/
void KisAsyncMergerTest::testFullRefreshWithClones()
{
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(image->bounds(), KoColor( Qt::white, colorSpace));
KisFilterSP filter = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(filter);
KisFilterConfiguration *configuration = filter->defaultConfiguration(0);
Q_ASSERT(configuration);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisFilterMaskSP invertMask1 = new KisFilterMask();
invertMask1->initSelection(paintLayer1);
invertMask1->setFilter(configuration);
KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer1, image, "clone_of_1", OPACITY_OPAQUE_U8);
/**
* The clone layer must have a projection to allow us
* to read what it got from its source. Just shift it.
*/
cloneLayer1->setX(10);
cloneLayer1->setY(10);
image->addNode(cloneLayer1, image->rootLayer());
image->addNode(paintLayer1, image->rootLayer());
image->addNode(invertMask1, paintLayer1);
QRect cropRect(image->bounds());
KisFullRefreshWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(image->rootLayer(), image->bounds());
merger.startMerge(walker);
// Wait for additional jobs generated by the clone are finished
image->waitForDone();
QRect filledRect(10, 10,
image->width() - cloneLayer1->x(),
image->height() - cloneLayer1->y());
const int pixelSize = device1->pixelSize();
const int numPixels = filledRect.width() * filledRect.height();
QByteArray bytes(numPixels * pixelSize, 13);
cloneLayer1->projection()->readBytes((quint8*)bytes.data(), filledRect);
KoColor desiredPixel(Qt::black, colorSpace);
quint8 *srcPtr = (quint8*)bytes.data();
quint8 *dstPtr = desiredPixel.data();
for(int i = 0; i < numPixels; i++) {
if(memcmp(srcPtr, dstPtr, pixelSize)) {
- qDebug() << "expected:" << dstPtr[0] << dstPtr[1] << dstPtr[2] << dstPtr[3];
- qDebug() << "result: " << srcPtr[0] << srcPtr[1] << srcPtr[2] << srcPtr[3];
+ dbgKrita << "expected:" << dstPtr[0] << dstPtr[1] << dstPtr[2] << dstPtr[3];
+ dbgKrita << "result: " << srcPtr[0] << srcPtr[1] << srcPtr[2] << srcPtr[3];
QFAIL("Failed to compare pixels");
}
srcPtr += pixelSize;
}
}
/*
+--------------+
|root |
| paint 2 |
| paint 1 |
+--------------+
*/
void KisAsyncMergerTest::testSubgraphingWithoutUpdatingParent()
{
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(image->bounds(), KoColor(Qt::white, colorSpace));
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
device2->fill(image->bounds(), KoColor(Qt::black, colorSpace));
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", 128, device2);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
image->initialRefreshGraph();
QImage refImage(QString(FILES_DATA_DIR) + QDir::separator() + "subgraphing_without_updating.png");
{
QImage resultImage = image->projection()->convertToQImage(0);
QCOMPARE(resultImage, refImage);
}
QRect cropRect(image->bounds());
KisRefreshSubtreeWalker walker(cropRect);
KisAsyncMerger merger;
walker.collectRects(paintLayer2, image->bounds());
merger.startMerge(walker);
{
QImage resultImage = image->projection()->convertToQImage(0);
QCOMPARE(resultImage, refImage);
}
}
QTEST_KDEMAIN(KisAsyncMergerTest, NoGUI)
diff --git a/krita/image/tests/kis_bsplines_test.cpp b/krita/image/tests/kis_bsplines_test.cpp
index 76e5bc34d7b..cc2116813f1 100644
--- a/krita/image/tests/kis_bsplines_test.cpp
+++ b/krita/image/tests/kis_bsplines_test.cpp
@@ -1,181 +1,181 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_bsplines_test.h"
#include <qtest_kde.h>
#include <cmath>
#include <bsplines/kis_bspline_1d.h>
#include <bsplines/kis_bspline_2d.h>
#include <bsplines/kis_nu_bspline_2d.h>
#include <kis_debug.h>
#include <kis_global.h>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
using namespace boost::accumulators;
using namespace KisBSplines;
struct FunctionOp {
float operator() (qreal x) {
return std::sqrt(x);
}
float operator() (qreal x, qreal y) const {
return std::sqrt(x) + y - pow2(x + y);
}
};
void KisBSplinesTest::test1D()
{
const qreal start = 1.0;
const qreal end = 11.0;
KisBSpline1D spline(start, end, 10, Natural);
spline.initializeSpline<FunctionOp>();
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
FunctionOp op;
for (qreal x = start; x < end; x += 0.01) {
qreal value = op(x);
qreal relError = (spline.value(x) - value) / value;
accum(relError);
if (relError > 0.10) {
- qDebug() << ppVar(x) << ppVar(op(x)) << ppVar(spline.value(x)) << ppVar(relError);
+ dbgKrita << ppVar(x) << ppVar(op(x)) << ppVar(spline.value(x)) << ppVar(relError);
}
}
- qDebug() << ppVar(count(accum));
- qDebug() << ppVar(mean(accum));
- qDebug() << ppVar(variance(accum));
- qDebug() << ppVar((min)(accum));
- qDebug() << ppVar((max)(accum));
+ dbgKrita << ppVar(count(accum));
+ dbgKrita << ppVar(mean(accum));
+ dbgKrita << ppVar(variance(accum));
+ dbgKrita << ppVar((min)(accum));
+ dbgKrita << ppVar((max)(accum));
qreal maxError = qMax(qAbs((min)(accum)), qAbs((max)(accum)));
QVERIFY(maxError < 0.10); // Error is less than 10%
}
void KisBSplinesTest::testEmpty1D()
{
const qreal start = 1.0;
const qreal end = 11.0;
KisBSpline1D spline(start, end, 10, Natural);
// just let it be destructed uninitialized
}
template <class Spline, class Op>
bool test2DSpline(const Spline &spline, const Op &op, qreal start, qreal end)
{
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
for (qreal y = start; y < end; y += 0.01) {
for (qreal x = start; x < end; x += 0.01) {
qreal value = op(x, y);
qreal relError = (spline.value(x, y) - value) / value;
accum(relError);
if (relError > 0.10) {
- qDebug() << ppVar(x) << ppVar(y) << ppVar(op(x, y)) << ppVar(spline.value(x, y)) << ppVar(relError);
+ dbgKrita << ppVar(x) << ppVar(y) << ppVar(op(x, y)) << ppVar(spline.value(x, y)) << ppVar(relError);
}
}
}
- qDebug() << ppVar(count(accum));
- qDebug() << ppVar(mean(accum));
- qDebug() << ppVar(variance(accum));
- qDebug() << ppVar((min)(accum));
- qDebug() << ppVar((max)(accum));
+ dbgKrita << ppVar(count(accum));
+ dbgKrita << ppVar(mean(accum));
+ dbgKrita << ppVar(variance(accum));
+ dbgKrita << ppVar((min)(accum));
+ dbgKrita << ppVar((max)(accum));
qreal maxError = qMax(qAbs((min)(accum)), qAbs((max)(accum)));
return maxError < 0.10; // Error is less than 10%
}
void KisBSplinesTest::test2D()
{
const qreal start = 1.0;
const qreal end = 11.0;
KisBSpline2D spline(start, end, 10, Natural,
start, end, 10, Natural);
FunctionOp op;
spline.initializeSpline(op);
QVERIFY(test2DSpline(spline, op, start, end));
- qDebug() << "Resampled";
+ dbgKrita << "Resampled";
QScopedPointer<KisBSpline2D> resampledSpline(
KisBSpline2D::createResampledSpline(spline, 7, 7));
QVERIFY(test2DSpline(*resampledSpline.data(), op, start, end));
}
void KisBSplinesTest::testEmpty2D()
{
const qreal start = 1.0;
const qreal end = 11.0;
KisBSpline2D spline(start, end, 10, Natural,
start, end, 10, Natural);
// just let it be destructed uninitialized
}
void KisBSplinesTest::testNU2D()
{
const qreal start = 1.0;
const qreal end = 11.0;
QVector<double> samples;
double v = start;
int i = 1;
do {
samples << v;
v += (0.1 * i++);
} while (v < end);
samples << end;
KisNUBSpline2D spline(samples, Natural,
samples, Natural);
FunctionOp op;
spline.initializeSpline(op);
QVERIFY(test2DSpline(spline, op, start, end));
- qDebug() << "Resampled";
+ dbgKrita << "Resampled";
QScopedPointer<KisBSpline2D> resampledSpline(
KisBSpline2D::createResampledSpline(spline, 7, 7));
QVERIFY(test2DSpline(*resampledSpline.data(), op, start, end));
}
QTEST_KDEMAIN(KisBSplinesTest, GUI)
diff --git a/krita/image/tests/kis_cage_transform_worker_test.cpp b/krita/image/tests/kis_cage_transform_worker_test.cpp
index 1406339e503..99090b0df29 100644
--- a/krita/image/tests/kis_cage_transform_worker_test.cpp
+++ b/krita/image/tests/kis_cage_transform_worker_test.cpp
@@ -1,330 +1,330 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_cage_transform_worker_test.h"
#include <qtest_kde.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
#include "testutil.h"
#include <kis_cage_transform_worker.h>
#include <algorithm>
void testCage(bool clockwise, bool unityTransform, bool benchmarkPrepareOnly = false, int pixelPrecision = 8, bool testQImage = false)
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_cage_transform.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
QRectF bounds(dev->exactBounds());
origPoints << bounds.topLeft();
origPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
origPoints << 0.5 * (bounds.topLeft() + bounds.bottomRight());
origPoints << 0.5 * (bounds.topRight() + bounds.bottomRight());
origPoints << bounds.bottomRight();
origPoints << bounds.bottomLeft();
if (!clockwise) {
std::reverse(origPoints.begin(), origPoints.end());
}
if (unityTransform) {
transfPoints = origPoints;
} else {
transfPoints << bounds.topLeft();
transfPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) +
(bounds.bottomLeft() - bounds.topLeft());
transfPoints << bounds.bottomLeft() +
(bounds.bottomLeft() - bounds.topLeft());
transfPoints << bounds.bottomLeft();
if (!clockwise) {
std::reverse(transfPoints.begin(), transfPoints.end());
}
}
KisCageTransformWorker worker(dev,
origPoints,
updater,
pixelPrecision);
QImage result;
QPointF srcQImageOffset(0, 0);
QPointF dstQImageOffset;
QBENCHMARK_ONCE {
if (!testQImage) {
worker.prepareTransform();
if (!benchmarkPrepareOnly) {
worker.setTransformedCage(transfPoints);
worker.run();
}
} else {
QImage srcImage(image);
image = QImage(image.size(), QImage::Format_ARGB32);
QPainter gc(&image);
gc.drawImage(QPoint(), srcImage);
image = image.convertToFormat(QImage::Format_ARGB32);
KisCageTransformWorker qimageWorker(image,
srcQImageOffset,
origPoints,
updater,
pixelPrecision);
qimageWorker.prepareTransform();
qimageWorker.setTransformedCage(transfPoints);
result = qimageWorker.runOnQImage(&dstQImageOffset);
}
}
QString testName = QString("%1_%2")
.arg(clockwise ? "clk" : "cclk")
.arg(unityTransform ? "unity" : "normal");
if (testQImage) {
QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage_qimage", testName));
} else if (!benchmarkPrepareOnly && pixelPrecision == 8) {
result = dev->convertToQImage(0);
QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage", testName));
}
}
void KisCageTransformWorkerTest::testCageClockwise()
{
testCage(true, false);
}
void KisCageTransformWorkerTest::testCageClockwisePrepareOnly()
{
testCage(true, false, true);
}
void KisCageTransformWorkerTest::testCageClockwisePixePrecision4()
{
testCage(true, false, false, 4);
}
void KisCageTransformWorkerTest::testCageClockwisePixePrecision8QImage()
{
testCage(true, false, false, 8, true);
}
void KisCageTransformWorkerTest::testCageCounterclockwise()
{
testCage(false, false);
}
void KisCageTransformWorkerTest::testCageClockwiseUnity()
{
testCage(true, true);
}
void KisCageTransformWorkerTest::testCageCounterclockwiseUnity()
{
testCage(false, true);
}
#include <QtGlobal>
QPointF generatePoint(const QRectF &rc)
{
qreal cx = qreal(qrand()) / RAND_MAX;
qreal cy = qreal(qrand()) / RAND_MAX;
QPointF diff = rc.bottomRight() - rc.topLeft();
QPointF pt = rc.topLeft() + QPointF(cx * diff.x(), cy * diff.y());
return pt;
}
void KisCageTransformWorkerTest::stressTestRandomCages()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_cage_transform.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
const int pixelPrecision = 8;
QRectF bounds(dev->exactBounds());
qsrand(1);
for (int numPoints = 4; numPoints < 15; numPoints+=5) {
for (int j = 0; j < 200; j++) {
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
- qDebug() << ppVar(j);
+ dbgKrita << ppVar(j);
for (int i = 0; i < numPoints; i++) {
origPoints << generatePoint(bounds);
transfPoints << generatePoint(bounds);
}
// no just hope it doesn't crash ;)
KisCageTransformWorker worker(dev,
origPoints,
updater,
pixelPrecision);
worker.prepareTransform();
worker.setTransformedCage(transfPoints);
worker.run();
}
}
}
#include "kis_green_coordinates_math.h"
void KisCageTransformWorkerTest::testUnityGreenCoordinates()
{
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
QRectF bounds(0,0,300,300);
origPoints << bounds.topLeft();
origPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
origPoints << 0.5 * (bounds.topLeft() + bounds.bottomRight());
origPoints << 0.5 * (bounds.topRight() + bounds.bottomRight());
origPoints << bounds.bottomRight();
origPoints << bounds.bottomLeft();
transfPoints = origPoints;
QVector<QPointF> points;
points << QPointF(10,10);
points << QPointF(140,10);
points << QPointF(140,140);
points << QPointF(10,140);
points << QPointF(10,160);
points << QPointF(140,160);
points << QPointF(140,290);
points << QPointF(10,290);
points << QPointF(160,160);
points << QPointF(290,160);
points << QPointF(290,290);
points << QPointF(160,290);
KisGreenCoordinatesMath cage;
cage.precalculateGreenCoordinates(origPoints, points);
cage.generateTransformedCageNormals(transfPoints);
QVector<QPointF> newPoints;
for (int i = 0; i < points.size(); i++) {
newPoints << cage.transformedPoint(i, transfPoints);
QCOMPARE(points[i], newPoints.last());
}
}
#include "kis_algebra_2d.h"
void KisCageTransformWorkerTest::testTransformAsBase()
{
QPointF t(1.0, 0.0);
QPointF b1(1.0, 0.0);
QPointF b2(2.0, 0.0);
QPointF result;
t = QPointF(1.0, 0.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(2.0, 0.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(2.0, 0.0));
t = QPointF(1.0, 0.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(0.0, 1.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(0.0, 1.0));
t = QPointF(1.0, 0.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(0.0, 2.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(0.0, 2.0));
t = QPointF(0.0, 1.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(2.0, 0.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(0.0, 2.0));
t = QPointF(0.0, 1.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(0.0, 1.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(-1.0, 0.0));
t = QPointF(0.0, 1.0);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(0.0, 2.0);
result = KisAlgebra2D::transformAsBase(t, b1, b2);
QCOMPARE(result, QPointF(-2.0, 0.0));
}
void KisCageTransformWorkerTest::testAngleBetweenVectors()
{
QPointF b1(1.0, 0.0);
QPointF b2(2.0, 0.0);
qreal result;
b1 = QPointF(1.0, 0.0);
b2 = QPointF(0.0, 1.0);
result = KisAlgebra2D::angleBetweenVectors(b1, b2);
QCOMPARE(result, M_PI_2);
b1 = QPointF(1.0, 0.0);
b2 = QPointF(std::sqrt(0.5), std::sqrt(0.5));
result = KisAlgebra2D::angleBetweenVectors(b1, b2);
QCOMPARE(result, M_PI / 4);
QTransform t;
t.rotateRadians(M_PI / 4);
QCOMPARE(t.map(b1), b2);
}
QTEST_KDEMAIN(KisCageTransformWorkerTest, GUI)
diff --git a/krita/image/tests/kis_clone_layer_test.cpp b/krita/image/tests/kis_clone_layer_test.cpp
index 524583836c7..8f90e5eb37c 100644
--- a/krita/image/tests/kis_clone_layer_test.cpp
+++ b/krita/image/tests/kis_clone_layer_test.cpp
@@ -1,394 +1,394 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_clone_layer_test.h"
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_clone_layer.h"
#include "kis_image.h"
#include "testutil.h"
void KisCloneLayerTest::testCreation()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "layer test");
KisLayerSP layer = new KisPaintLayer(image, "clone test", OPACITY_OPAQUE_U8);
KisCloneLayer test(layer, image, "clonetest", OPACITY_OPAQUE_U8);
}
KisImageSP createImage()
{
/*
+-----------------+
|root |
| group 1 <-----+ |
| paint 3 | |
| paint 1 | |
| group 2 | |
| clone_of_g1 -+ |
| paint 2 |
+-----------------+
*/
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
QRect fillRect(10,10,100,100);
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
device1->fill(fillRect, KoColor( Qt::white, colorSpace));
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisLayerSP groupLayer1 = new KisGroupLayer(image, "group1", OPACITY_OPAQUE_U8);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
KisPaintDeviceSP device3 = new KisPaintDevice(colorSpace);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8, device3);
KisLayerSP cloneLayer1 = new KisCloneLayer(groupLayer1, image, "clone_of_g1", OPACITY_OPAQUE_U8);
cloneLayer1->setX(10);
cloneLayer1->setY(10);
KisLayerSP groupLayer2 = new KisGroupLayer(image, "group2", OPACITY_OPAQUE_U8);
image->addNode(groupLayer2, image->rootLayer());
image->addNode(groupLayer1, image->rootLayer());
image->addNode(paintLayer2, groupLayer2);
image->addNode(cloneLayer1, groupLayer2);
image->addNode(paintLayer1, groupLayer1);
image->addNode(paintLayer3, groupLayer1);
return image;
}
KisNodeSP groupLayer1(KisImageSP image) {
KisNodeSP node = image->root()->lastChild();
Q_ASSERT(node->name() == "group1");
return node;
}
KisNodeSP paintLayer1(KisImageSP image) {
KisNodeSP node = groupLayer1(image)->firstChild();
Q_ASSERT(node->name() == "paint1");
return node;
}
void KisCloneLayerTest::testOriginalUpdates()
{
const QRect nullRect(QPoint(0, 0), QPoint(-1, -1));
KisImageSP image = createImage();
KisNodeSP root = image->root();
QCOMPARE(root->projection()->exactBounds(), nullRect);
paintLayer1(image)->setDirty(image->bounds());
image->waitForDone();
const QRect expectedRect(10,10,110,110);
QCOMPARE(root->projection()->exactBounds(), expectedRect);
}
void KisCloneLayerTest::testOriginalUpdatesOutOfBounds()
{
const QRect nullRect(QPoint(0, 0), QPoint(-1, -1));
KisImageSP image = createImage();
KisNodeSP root = image->root();
QCOMPARE(root->projection()->exactBounds(), nullRect);
QRect fillRect(-10,-10,10,10);
paintLayer1(image)->paintDevice()->fill(fillRect, KoColor(Qt::white, image->colorSpace()));
paintLayer1(image)->setDirty(fillRect);
image->waitForDone();
const QRect expectedRect(0,0,10,10);
QCOMPARE(root->projection()->exactBounds(), expectedRect);
QCOMPARE(groupLayer1(image)->projection()->exactBounds(), fillRect);
}
void KisCloneLayerTest::testOriginalRefresh()
{
const QRect nullRect(QPoint(0, 0), QPoint(-1, -1));
KisImageSP image = createImage();
KisNodeSP root = image->root();
QCOMPARE(root->projection()->exactBounds(), nullRect);
image->refreshGraph();
image->waitForDone();
const QRect expectedRect(10,10,110,110);
QCOMPARE(root->projection()->exactBounds(), expectedRect);
}
#include "commands/kis_image_commands.h"
void KisCloneLayerTest::testRemoveSourceLayer()
{
KisImageSP image = createImage();
KisNodeSP root = image->root();
KisNodeSP group1 = groupLayer1(image);
/**
* We are checking that noone keeps a pointer to the
* source layer after it is deleted. Uncomment the first
* line to see how perfectly it crashed if removing the
* source directly through the node facade
*/
//image->removeNode(group1);
KUndo2Command *cmd = new KisImageLayerRemoveCommand(image, group1);
cmd->redo();
delete cmd;
// We are veeeery bad! Never do like this! >:)
- qDebug() << "Ref. count:" << group1->refCount();
+ dbgKrita << "Ref. count:" << group1->refCount();
KisNodeWSP group1_wsp = group1;
KisNode *group1_ptr = group1.data();
group1 = 0;
if(group1_wsp.isValid()) {
group1_wsp = 0;
while(group1_ptr->refCount()) group1_ptr->deref();
delete group1_ptr;
}
// Are we crashing?
image->refreshGraph();
image->waitForDone();
}
void KisCloneLayerTest::testRemoveSourceLayerParent()
{
KisImageSP image = createImage();
KisNodeSP root = image->root();
KisNodeSP group1 = TestUtil::findNode(root, "group1");
KisNodeSP group2 = TestUtil::findNode(root, "group2");
KisNodeSP cloneLayer1 = TestUtil::findNode(root, "clone_of_g1");
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer4 = new KisCloneLayer(paintLayer4, image, "clone_of_p4", OPACITY_OPAQUE_U8);
image->addNode(paintLayer4, group1);
image->addNode(cloneLayer4, group2);
Q_ASSERT(group1->lastChild() == KisNodeSP(paintLayer4));
Q_ASSERT(group2->lastChild() == KisNodeSP(cloneLayer4));
Q_ASSERT(TestUtil::findNode(group2, "clone_of_g1") == KisNodeSP(cloneLayer1));
Q_ASSERT(TestUtil::findNode(group2, "clone_of_p4") == KisNodeSP(cloneLayer4));
/**
* This test checks if the node reincarnates when the parent of
* the clone's source layer is removed.
*
* Here both group1 and paint4 are removed.
*/
KUndo2Command *cmd = new KisImageLayerRemoveCommand(image, group1);
cmd->redo();
delete cmd;
KisNodeSP newCloneLayer1 = TestUtil::findNode(group2, "clone_of_g1");
KisNodeSP newCloneLayer4 = TestUtil::findNode(group2, "clone_of_p4");
QVERIFY(newCloneLayer1 != KisNodeSP(cloneLayer1));
QVERIFY(dynamic_cast<KisPaintLayer*>(newCloneLayer1.data()));
QVERIFY(newCloneLayer4 != KisNodeSP(cloneLayer4));
QVERIFY(dynamic_cast<KisPaintLayer*>(newCloneLayer4.data()));
}
void KisCloneLayerTest::testUndoingRemovingSource()
{
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer1, image, "clone_of_p1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1);
image->addNode(cloneLayer1);
QCOMPARE(image->root()->lastChild(), KisNodeSP(cloneLayer1));
QCOMPARE(image->root()->lastChild()->name(), QString("clone_of_p1"));
QCOMPARE(image->root()->firstChild()->name(), QString("paint1"));
KUndo2Command *cmd1 = new KisImageLayerRemoveCommand(image, paintLayer1);
image->barrierLock();
cmd1->redo();
image->unlock();
QCOMPARE(image->root()->lastChild()->name(), QString("clone_of_p1"));
QVERIFY(image->root()->lastChild() != KisNodeSP(cloneLayer1));
KisNodeSP reincarnatedLayer = image->root()->lastChild();
KUndo2Command *cmd2 = new KisImageLayerRemoveCommand(image, reincarnatedLayer);
image->barrierLock();
cmd2->redo();
image->unlock();
image->barrierLock();
cmd2->undo();
image->unlock();
image->barrierLock();
cmd1->undo();
image->unlock();
image->barrierLock();
cmd1->redo();
image->unlock();
QCOMPARE(image->root()->lastChild()->name(), QString("clone_of_p1"));
QCOMPARE(image->root()->lastChild(), reincarnatedLayer);
QVERIFY(image->root()->lastChild() != KisNodeSP(cloneLayer1));
cmd2->redo();
delete cmd1;
delete cmd2;
}
void KisCloneLayerTest::testDuplicateGroup()
{
KisImageSP image = createImage();
KisNodeSP root = image->root();
KisNodeSP group1 = groupLayer1(image);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer4 = new KisCloneLayer(paintLayer4, image, "clone_of_p4", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer4 = new KisGroupLayer(image, "group4", OPACITY_OPAQUE_U8);
image->addNode(paintLayer4, group1);
image->addNode(cloneLayer4, groupLayer4);
image->addNode(groupLayer4, group1);
Q_ASSERT(group1->lastChild() == KisNodeSP(groupLayer4));
Q_ASSERT(group1->lastChild()->lastChild() == KisNodeSP(cloneLayer4));
Q_ASSERT(group1->lastChild()->prevSibling() == KisNodeSP(paintLayer4));
KisNodeSP copyGroup1 = group1->clone();
KisNodeSP copyCloneLayer4 = copyGroup1->lastChild()->lastChild();
KisNodeSP copyGroupLayer4 = copyGroup1->lastChild();
KisNodeSP copyPaintLayer4 = copyGroup1->lastChild()->prevSibling();
QCOMPARE(copyPaintLayer4->name(), QString("paint4"));
QCOMPARE(copyGroupLayer4->name(), QString("group4"));
QCOMPARE(copyCloneLayer4->name(), QString("clone_of_p4"));
QVERIFY(copyPaintLayer4 != KisNodeSP(paintLayer4));
QVERIFY(copyGroupLayer4 != KisNodeSP(groupLayer4));
QVERIFY(copyCloneLayer4 != KisNodeSP(cloneLayer4));
KisCloneLayerSP newClone = dynamic_cast<KisCloneLayer*>(copyCloneLayer4.data());
/**
* The newly created clone should now point to the *newly created*
* paint layer, not to the previos one.
*/
QCOMPARE(KisNodeSP(newClone->copyFrom()), copyPaintLayer4);
}
struct CyclingTester {
CyclingTester() {
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
image = new KisImage(0, 128, 128, colorSpace, "clones test");
groupLayer1 = new KisGroupLayer(image, "group1", OPACITY_OPAQUE_U8);
groupLayer2 = new KisGroupLayer(image, "group2", OPACITY_OPAQUE_U8);
cloneOfGroup1 = new KisCloneLayer(groupLayer1, image, "clone_of_g1", OPACITY_OPAQUE_U8);
cloneOfGroup2 = new KisCloneLayer(groupLayer2, image, "clone_of_g2", OPACITY_OPAQUE_U8);
}
void reset() {
image->removeNode(groupLayer1);
image->removeNode(groupLayer2);
image->removeNode(cloneOfGroup1);
image->removeNode(cloneOfGroup2);
}
KisImageSP image;
KisLayerSP groupLayer1;
KisLayerSP groupLayer2;
KisLayerSP cloneOfGroup1;
KisLayerSP cloneOfGroup2;
};
inline void addIfNotPresent(KisNodeSP node, CyclingTester &t, KisNodeSP group1Child, KisNodeSP group2Child)
{
if(node != group1Child && node != group2Child) {
t.image->addNode(node);
}
}
/**
* group1 <-- adding \p group1Child here
* group2 -- has \p group2Child before addition
*/
inline void testCyclingCase(CyclingTester &t, KisNodeSP group1Child, KisNodeSP group2Child, bool expected)
{
addIfNotPresent(t.groupLayer2, t, group1Child, group2Child);
addIfNotPresent(t.cloneOfGroup1, t, group1Child, group2Child);
addIfNotPresent(t.cloneOfGroup2, t, group1Child, group2Child);
t.image->addNode(t.groupLayer1);
if(group2Child) {
t.image->addNode(group2Child, t.groupLayer2);
}
QCOMPARE(t.groupLayer1->allowAsChild(group1Child), expected);
t.reset();
}
void KisCloneLayerTest::testCyclingGroupLayer()
{
CyclingTester t;
testCyclingCase(t, t.groupLayer2, 0, true);
testCyclingCase(t, t.groupLayer2, t.cloneOfGroup1, false);
testCyclingCase(t, t.cloneOfGroup1, 0, false);
testCyclingCase(t, t.cloneOfGroup1, t.cloneOfGroup2, false);
testCyclingCase(t, t.cloneOfGroup2, 0, true);
testCyclingCase(t, t.cloneOfGroup2, t.cloneOfGroup1, false);
}
QTEST_KDEMAIN(KisCloneLayerTest, GUI)
diff --git a/krita/image/tests/kis_convolution_painter_test.cpp b/krita/image/tests/kis_convolution_painter_test.cpp
index eecf29688a5..bec3386bca5 100644
--- a/krita/image/tests/kis_convolution_painter_test.cpp
+++ b/krita/image/tests/kis_convolution_painter_test.cpp
@@ -1,440 +1,440 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_convolution_painter_test.h"
#include <qtest_kde.h>
#include <QBitArray>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_convolution_painter.h"
#include "kis_convolution_kernel.h"
#include <kis_gaussian_kernel.h>
#include <kis_mask_generator.h>
#include "testutil.h"
KisPaintDeviceSP initAsymTestDevice(QRect &imageRect, int &pixelSize, QByteArray &initialData)
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
pixelSize = dev->pixelSize();
imageRect = QRect(0,0,5,5);
initialData.resize(25 * pixelSize);
quint8 *ptr = (quint8*) initialData.data();
for(int i = 0; i < 25; i++) {
KoColor pixel(QColor(i,i,i,255), dev->colorSpace());
memcpy(ptr, pixel.data(), pixelSize);
ptr += pixelSize;
}
dev->writeBytes((const quint8*)initialData.constData(), imageRect);
return dev;
}
Matrix<qreal, 3, 3> initSymmFilter(qreal &offset, qreal &factor)
{
Matrix<qreal, 3, 3> filter;
filter(0,0) = 1.0 / 21;
filter(0,1) = 3.0 / 21;
filter(0,2) = 1.0 / 21;
filter(1,0) = 3.0 / 21;
filter(1,1) = 5.0 / 21;
filter(1,2) = 3.0 / 21;
filter(2,0) = 1.0 / 21;
filter(2,1) = 3.0 / 21;
filter(2,2) = 1.0 / 21;
offset = 0.0;
factor = 1.0;
return filter;
}
Matrix<qreal, 3, 3> initAsymmFilter(qreal &offset, qreal &factor)
{
Matrix<qreal, 3, 3> filter;
filter(0,0) = 1.0;
filter(1,0) = 2.0;
filter(2,0) = 1.0;
filter(0,1) = 0.0;
filter(1,1) = 1.0;
filter(2,1) = 0.0;
filter(0,2) =-1.0;
filter(1,2) =-2.0;
filter(2,2) =-1.0;
offset = 0.0;
factor = 1.0;
return filter;
}
void printPixel(QString prefix, int pixelSize, quint8 *data) {
QString str = prefix;
for(int i = 0; i < pixelSize; i++) {
str += ' ';
str += QString::number(data[i]);
}
- qDebug() << str;
+ dbgKrita << str;
}
void KisConvolutionPainterTest::testIdentityConvolution()
{
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(qimage, 0, 0, 0);
KisConvolutionKernelSP kernel = new KisConvolutionKernel(3, 3, 0, 0);
kernel->data()(0) = 0;
kernel->data()(1) = 0;
kernel->data()(2) = 0;
kernel->data()(3) = 0;
kernel->data()(4) = 1;
kernel->data()(5) = 0;
kernel->data()(6) = 0;
kernel->data()(7) = 0;
kernel->data()(8) = 0;
KisConvolutionPainter gc(dev);
gc.beginTransaction();
gc.applyMatrix(kernel, dev, QPoint(0, 0), QPoint(0, 0), QSize(qimage.width(), qimage.height()));
gc.deleteTransaction();
QImage resultImage = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height());
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, qimage, resultImage)) {
resultImage.save("identity_convolution.png");
QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisConvolutionPainterTest::testSymmConvolution()
{
qreal offset = 0.0;
qreal factor = 1.0;
Matrix<qreal, 3, 3> filter = initSymmFilter(offset, factor);
QRect imageRect;
int pixelSize = 0;
QByteArray initialData;
KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData);
KisConvolutionKernelSP kernel =
KisConvolutionKernel::fromMatrix(filter, offset, factor);
KisConvolutionPainter gc(dev);
gc.beginTransaction();
QRect filterRect = imageRect.adjusted(1,1,-1,-1);
gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(),
filterRect.size());
gc.deleteTransaction();
QByteArray resultData(initialData.size(), 0);
dev->readBytes((quint8*)resultData.data(), imageRect);
QCOMPARE(resultData, initialData);
}
void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags)
{
qreal offset = 0.0;
qreal factor = 1.0;
Matrix<qreal, 3, 3> filter = initAsymmFilter(offset, factor);
QRect imageRect;
int pixelSize = -1;
QByteArray initialData;
KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData);
KisConvolutionKernelSP kernel =
KisConvolutionKernel::fromMatrix(filter, offset, factor);
KisConvolutionPainter gc(dev);
gc.beginTransaction();
gc.setChannelFlags(channelFlags);
QRect filterRect = imageRect.adjusted(1,1,-1,-1);
gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(),
filterRect.size());
gc.deleteTransaction();
QByteArray resultData(initialData.size(), 0);
dev->readBytes((quint8*)resultData.data(), imageRect);
QRect filteredRect = imageRect.adjusted(1, 1, -1, -1);
quint8 *srcPtr = (quint8*) initialData.data();
quint8 *resPtr = (quint8*) resultData.data();
for(int row = 0; row < imageRect.height(); row++) {
for(int col = 0; col < imageRect.width(); col++) {
bool isFiltered = filteredRect.contains(col, row);
int pixelValue = 8 + row * imageRect.width() + col;
KoColor filteredPixel(QColor(pixelValue, pixelValue, pixelValue, 255), dev->colorSpace());
KoColor resultPixel(dev->colorSpace());
for(int j = 0; j < pixelSize; j++) {
resultPixel.data()[j] = isFiltered && channelFlags[j] ?
filteredPixel.data()[j] : srcPtr[j];
}
if(memcmp(resPtr, resultPixel.data(), pixelSize)) {
printPixel("Actual: ", pixelSize, resPtr);
printPixel("Expected:", pixelSize, resultPixel.data());
QFAIL("Failed to filter area");
}
srcPtr += pixelSize;
resPtr += pixelSize;
}
}
}
void KisConvolutionPainterTest::testAsymmAllChannels()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipRed()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[2] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipGreen()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[1] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipBlue()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[0] = false;
testAsymmConvolutionImp(channelFlags);
}
void KisConvolutionPainterTest::testAsymmSkipAlpha()
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
channelFlags[3] = false;
testAsymmConvolutionImp(channelFlags);
}
// #include <valgrind/callgrind.h>
void KisConvolutionPainterTest::benchmarkConvolution()
{
QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QRect imageRect(QPoint(), referenceImage.size());
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
int diameter = 1;
for (int i = 0; i < 10; i++) {
KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2, false);
KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas);
KisConvolutionPainter gc(dev);
QTime timer; timer.start();
// CALLGRIND_START_INSTRUMENTATION;
gc.beginTransaction();
gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(),
imageRect.size());
gc.deleteTransaction();
// CALLGRIND_STOP_INSTRUMENTATION;
- qDebug() << "Diameter:" << diameter << "time:" << timer.elapsed();
+ dbgKrita << "Diameter:" << diameter << "time:" << timer.elapsed();
if(diameter < 10) {
diameter += 2;
} else {
diameter += 8;
}
}
}
void KisConvolutionPainterTest::testGaussianBase(KisPaintDeviceSP dev, bool useFftw, const QString &prefix)
{
QBitArray channelFlags =
KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true);
KisPainter gc(dev);
qreal horizontalRadius = 5, verticalRadius = 5;
for(int i = 0; i < 3 ; i++, horizontalRadius+=5, verticalRadius+=5)
{
QTime timer;
timer.start();
gc.beginTransaction();
if (( horizontalRadius > 0 ) && ( verticalRadius > 0 )) {
KisPaintDeviceSP interm = new KisPaintDevice(dev->colorSpace());
KisConvolutionKernelSP kernelHoriz = KisGaussianKernel::createHorizontalKernel(horizontalRadius);
KisConvolutionKernelSP kernelVertical = KisGaussianKernel::createVerticalKernel(verticalRadius);
const QRect applyRect = dev->exactBounds();
KisConvolutionPainter::TestingEnginePreference enginePreference =
useFftw ?
KisConvolutionPainter::FFTW :
KisConvolutionPainter::SPATIAL;
KisConvolutionPainter horizPainter(interm, enginePreference);
horizPainter.setChannelFlags(channelFlags);
horizPainter.applyMatrix(kernelHoriz, dev,
applyRect.topLeft() - QPoint(0, verticalRadius),
applyRect.topLeft() - QPoint(0, verticalRadius),
applyRect.size() + QSize(0, 2 * verticalRadius),
BORDER_REPEAT);
KisConvolutionPainter verticalPainter(dev, enginePreference);
verticalPainter.setChannelFlags(channelFlags);
verticalPainter.applyMatrix(kernelVertical, interm,
applyRect.topLeft(),
applyRect.topLeft(),
applyRect.size(), BORDER_REPEAT);
QImage result = dev->convertToQImage(0, applyRect.x(), applyRect.y(), applyRect.width(), applyRect.height());
QString engine = useFftw ? "fftw" : "spatial";
QString testCaseName = QString("test_gaussian_%1_%2_%3.png").arg(horizontalRadius).arg(verticalRadius).arg(engine);
TestUtil::checkQImage(result,
"convolution_painter_test",
QString("gaussian_") + prefix,
testCaseName);
gc.revertTransaction();
}
- qDebug() << "Elapsed time:" << timer.elapsed() << "ms";
+ dbgKrita << "Elapsed time:" << timer.elapsed() << "ms";
}
}
void KisConvolutionPainterTest::testGaussian(bool useFftw)
{
QImage referenceImage(TestUtil::fetchDataFileLazy("kritaTransparent.png"));
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
testGaussianBase(dev, useFftw, "");
}
void KisConvolutionPainterTest::testGaussianSpatial()
{
testGaussian(false);
}
void KisConvolutionPainterTest::testGaussianFFTW()
{
testGaussian(true);
}
void KisConvolutionPainterTest::testGaussianSmall(bool useFftw)
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KoColor c(Qt::yellow, dev->colorSpace());
for (int i = 0; i < 50; i++) {
quint8 baseOpacity = 75;
KoColor c(Qt::magenta, dev->colorSpace());
for (int j = 0; j <= 6; j++) {
c.setOpacity(static_cast<quint8>(baseOpacity + 30 * j));
dev->setPixel(i + j, i, c);
}
}
testGaussianBase(dev, useFftw, "reduced");
}
void KisConvolutionPainterTest::testGaussianSmallSpatial()
{
testGaussianSmall(false);
}
void KisConvolutionPainterTest::testGaussianSmallFFTW()
{
testGaussianSmall(true);
}
void KisConvolutionPainterTest::testGaussianDetails(bool useFftw)
{
QImage referenceImage(TestUtil::fetchDataFileLazy("resolution_test.png"));
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
dev->convertFromQImage(referenceImage, 0, 0, 0);
testGaussianBase(dev, useFftw, "details");
}
void KisConvolutionPainterTest::testGaussianDetailsSpatial()
{
testGaussianDetails(false);
}
void KisConvolutionPainterTest::testGaussianDetailsFFTW()
{
testGaussianDetails(true);
}
QTEST_KDEMAIN(KisConvolutionPainterTest, GUI)
diff --git a/krita/image/tests/kis_cs_conversion_test.cpp b/krita/image/tests/kis_cs_conversion_test.cpp
index 89201dc7738..84fd7fea58c 100644
--- a/krita/image/tests/kis_cs_conversion_test.cpp
+++ b/krita/image/tests/kis_cs_conversion_test.cpp
@@ -1,102 +1,102 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_cs_conversion_test.h"
#include <qtest_kde.h>
#include <QTime>
#include <KoColor.h>
#include <KoColorSpace.h>
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisCsConversionTest::testColorSpaceConversion()
{
QTime t;
t.start();
QList<const KoColorSpace*> colorSpaces = TestUtil::allColorSpaces();
int failedColorSpaces = 0;
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
foreach(const KoColorSpace * srcCs, colorSpaces) {
foreach(const KoColorSpace * dstCs, colorSpaces) {
KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->move(10, 10); // Unalign with tile boundaries
dev->convertTo(dstCs);
if (dev->exactBounds() != QRect(10, 10, image.width(), image.height())) {
logFailure("bounds", srcCs, dstCs);
failedColorSpaces++;
}
if (dev->pixelSize() != dstCs->pixelSize()) {
logFailure("pixelsize", srcCs, dstCs);
failedColorSpaces++;
}
if (!(*dev->colorSpace() == *dstCs)) {
logFailure("dest cs", srcCs, dstCs);
failedColorSpaces++;
}
}
}
- qDebug() << colorSpaces.size() * colorSpaces.size()
+ dbgKrita << colorSpaces.size() * colorSpaces.size()
<< "conversions"
<< " done in "
<< t.elapsed()
<< "ms";
if (failedColorSpaces > 0) {
QFAIL(QString("Failed conversions %1, see log for details.").arg(failedColorSpaces).toLatin1());
}
}
QTEST_KDEMAIN(KisCsConversionTest, GUI)
diff --git a/krita/image/tests/kis_filter_weights_applicator_test.cpp b/krita/image/tests/kis_filter_weights_applicator_test.cpp
index 865d2053594..2366b980a09 100644
--- a/krita/image/tests/kis_filter_weights_applicator_test.cpp
+++ b/krita/image/tests/kis_filter_weights_applicator_test.cpp
@@ -1,460 +1,460 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_filter_weights_applicator_test.h"
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
//#define DEBUG_ENABLED
#include "kis_filter_weights_applicator.h"
void debugSpan(const KisFilterWeightsApplicator::BlendSpan &span)
{
- qDebug() << ppVar(span.weights->centerIndex);
+ dbgKrita << ppVar(span.weights->centerIndex);
for (int i = 0; i < span.weights->span; i++) {
- qDebug() << "Weights" << i << span.weights->weight[i];
+ dbgKrita << "Weights" << i << span.weights->weight[i];
}
- qDebug() << ppVar(span.firstBlendPixel);
- qDebug() << ppVar(span.offset);
- qDebug() << ppVar(span.offsetInc);
+ dbgKrita << ppVar(span.firstBlendPixel);
+ dbgKrita << ppVar(span.offset);
+ dbgKrita << ppVar(span.offsetInc);
}
void testSpan(qreal scale, qreal dx, int dst_l,
int expectedFirstPixel,
qreal expectedOffset,
qreal expectedOffsetInc)
{
KisFilterStrategy *filter = new KisBilinearFilterStrategy();
KisFilterWeightsBuffer buf(filter, qAbs(scale));
KisFilterWeightsApplicator applicator(0, 0, scale, 0.0, dx, false);
KisFilterWeightsApplicator::BlendSpan span;
span = applicator.calculateBlendSpan(dst_l, 0, &buf);
//debugSpan(span);
if (span.firstBlendPixel != expectedFirstPixel ||
span.offset != KisFixedPoint(expectedOffset) ||
span.offsetInc != KisFixedPoint(expectedOffsetInc)) {
- qDebug() << "Failed to generate a span:";
- qDebug() << ppVar(scale) << ppVar(dx) << ppVar(dst_l);
- qDebug() << ppVar(span.firstBlendPixel) << ppVar(expectedFirstPixel);
- qDebug() << ppVar(span.offset) << ppVar(KisFixedPoint(expectedOffset));
- qDebug() << ppVar(span.offsetInc) << ppVar(KisFixedPoint(expectedOffsetInc));
+ dbgKrita << "Failed to generate a span:";
+ dbgKrita << ppVar(scale) << ppVar(dx) << ppVar(dst_l);
+ dbgKrita << ppVar(span.firstBlendPixel) << ppVar(expectedFirstPixel);
+ dbgKrita << ppVar(span.offset) << ppVar(KisFixedPoint(expectedOffset));
+ dbgKrita << ppVar(span.offsetInc) << ppVar(KisFixedPoint(expectedOffsetInc));
QFAIL("fail");
}
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_2_0_Aligned()
{
testSpan(2.0, 0.0, 0, -1, 0.25, 1.0);
testSpan(2.0, 0.0, 1, 0, 0.75, 1.0);
testSpan(2.0, 0.0, -1, -1, 0.75, 1.0);
testSpan(2.0, 0.0, -2, -2, 0.25, 1.0);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_2_0_Shift_0_5()
{
testSpan(2.0, 0.5, 0, -1, 0.5, 1.0);
testSpan(2.0, 0.5, 1, -1, 0.0, 1.0);
testSpan(2.0, 0.5, -1, -2, 0.0, 1.0);
testSpan(2.0, 0.5, -2, -2, 0.5, 1.0);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_2_0_Shift_0_75()
{
testSpan(2.0, 0.75, 0, -1, 0.625, 1.0);
testSpan(2.0, 0.75, 1, -1, 0.125, 1.0);
testSpan(2.0, 0.75, -1, -2, 0.125, 1.0);
testSpan(2.0, 0.75, -2, -2, 0.625, 1.0);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Aligned()
{
testSpan(0.5, 0.0, 0, -1, 0.25, 0.5);
testSpan(0.5, 0.0, 1, 1, 0.25, 0.5);
testSpan(0.5, 0.0, -1, -3, 0.25, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_0_5()
{
testSpan(0.5, 0.5, 0, -2, 0.25, 0.5);
testSpan(0.5, 0.5, 1, 0, 0.25, 0.5);
testSpan(0.5, 0.5, -1, -4, 0.25, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_0_25()
{
testSpan(0.5, 0.25, 0, -2, 0.0, 0.5);
testSpan(0.5, 0.25, 1, 0, 0.0, 0.5);
testSpan(0.5, 0.25, -1, -4, 0.0, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_0_375()
{
testSpan(0.5, 0.375, 0, -2, 0.125, 0.5);
testSpan(0.5, 0.375, 1, 0, 0.125, 0.5);
testSpan(0.5, 0.375, -1, -4, 0.125, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_m0_5()
{
testSpan(0.5, -0.5, 0, 0, 0.25, 0.5);
testSpan(0.5, -0.5, 1, 2, 0.25, 0.5);
testSpan(0.5, -0.5, -1, -2, 0.25, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_m0_25()
{
testSpan(0.5, -0.25, 0, -1, 0.0, 0.5);
testSpan(0.5, -0.25, 1, 1, 0.0, 0.5);
testSpan(0.5, -0.25, -1, -3, 0.0, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_m0_375()
{
testSpan(0.5, -0.375, 0, 0, 0.375, 0.5);
testSpan(0.5, -0.375, 1, 2, 0.375, 0.5);
testSpan(0.5, -0.375, -1, -2, 0.375, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_1_0_Aligned_Mirrored()
{
testSpan(-1.0, 0.0, 0, -2, 0.0, 1.0);
testSpan(-1.0, 0.0, 1, -3, 0.0, 1.0);
testSpan(-1.0, 0.0, -1, -1, 0.0, 1.0);
testSpan(-1.0, 0.0, -2, 0, 0.0, 1.0);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Aligned_Mirrored()
{
testSpan(-0.5, 0.0, 0, -3, 0.25, 0.5);
testSpan(-0.5, 0.0, 1, -5, 0.25, 0.5);
testSpan(-0.5, 0.0, -1, -1, 0.25, 0.5);
testSpan(-0.5, 0.0, -2, 1, 0.25, 0.5);
}
void KisFilterWeightsApplicatorTest::testSpan_Scale_0_5_Shift_0_125_Mirrored()
{
testSpan(-0.5, 0.125, 0, -3, 0.125, 0.5);
testSpan(-0.5, 0.125, 1, -5, 0.125, 0.5);
testSpan(-0.5, 0.125, -1, -1, 0.125, 0.5);
testSpan(-0.5, 0.125, -2, 1, 0.125, 0.5);
}
void printPixels(KisPaintDeviceSP dev, int x0, int len, bool horizontal)
{
for (int i = x0; i < x0 + len; i++) {
QColor c;
int x = horizontal ? i : 0;
int y = horizontal ? 0 : i;
dev->pixel(x, y, &c);
- qDebug() << "px" << x << y << "|" << c.red() << c.green() << c.blue() << c.alpha();
+ dbgKrita << "px" << x << y << "|" << c.red() << c.green() << c.blue() << c.alpha();
}
}
void checkRA(KisPaintDeviceSP dev, int x0, int len, quint8 r[], quint8 a[], bool horizontal)
{
for (int i = 0; i < len; i++) {
QColor c;
int x = horizontal ? x0 + i : 0;
int y = horizontal ? 0 : x0 + i;
dev->pixel(x, y, &c);
if (c.red() != r[i] ||
c.alpha() != a[i]) {
- qDebug() << "Failed to compare RA channels:" << ppVar(x0 + i);
- qDebug() << "Red:" << c.red() << "Expected:" << r[i];
- qDebug() << "Alpha:" << c.alpha() << "Expected:" << a[i];
+ dbgKrita << "Failed to compare RA channels:" << ppVar(x0 + i);
+ dbgKrita << "Red:" << c.red() << "Expected:" << r[i];
+ dbgKrita << "Alpha:" << c.alpha() << "Expected:" << a[i];
QFAIL("failed");
}
}
}
void testLineImpl(qreal scale, qreal dx, quint8 expR[], quint8 expA[], int x0, int len, bool clampToEdge, bool horizontal)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisFilterStrategy *filter = new KisBilinearFilterStrategy();
KisFilterWeightsBuffer buf(filter, qAbs(scale));
KisFilterWeightsApplicator applicator(dev, dev, scale, 0.0, dx, clampToEdge);
for (int i = 0; i < 4; i++) {
int x = horizontal ? i : 0;
int y = horizontal ? 0 : i;
dev->setPixel(x,y,QColor(10 + i * 10, 20 + i * 10, 40 + i * 10));
}
{
quint8 r[] = { 0, 10, 20, 30, 40, 0, 0};
quint8 a[] = { 0,255,255,255,255, 0, 0};
checkRA(dev, -1, 6, r, a, horizontal);
}
KisFilterWeightsApplicator::LinePos srcPos(0,4);
KisFilterWeightsApplicator::LinePos dstPos;
if (horizontal) {
dstPos = applicator.processLine<KisHLineIteratorSP>(srcPos,0,&buf, filter->support());
} else {
dstPos = applicator.processLine<KisVLineIteratorSP>(srcPos,0,&buf, filter->support());
}
QRect rc = dev->exactBounds();
if (horizontal) {
QVERIFY(rc.left() >= dstPos.start());
QVERIFY(rc.left() + rc.width() <= dstPos.end());
} else {
QVERIFY(rc.top() >= dstPos.start());
QVERIFY(rc.top() + rc.height() <= dstPos.end());
}
//printPixels(dev, x0, len, horizontal);
checkRA(dev, x0, len, expR, expA, horizontal);
}
void testLine(qreal scale, qreal dx, quint8 expR[], quint8 expA[], int x0, int len, bool clampToEdge = false)
{
testLineImpl(scale, dx, expR, expA, x0, len, clampToEdge, true);
testLineImpl(scale, dx, expR, expA, x0, len, clampToEdge, false);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Aligned()
{
qreal scale = 1.0;
qreal dx = 0.0;
quint8 r[] = { 0, 10, 20, 30, 40, 0, 0};
quint8 a[] = { 0,255,255,255,255, 0, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Shift_0_5()
{
qreal scale = 1.0;
qreal dx = 0.5;
quint8 r[] = { 0, 10, 15, 25, 35, 40, 0};
quint8 a[] = { 0,128,255,255,255,127, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Shift_m0_5()
{
qreal scale = 1.0;
qreal dx = -0.5;
quint8 r[] = { 10, 15, 25, 35, 40, 0, 0};
quint8 a[] = {128,255,255,255,127, 0, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Shift_0_25()
{
qreal scale = 1.0;
qreal dx = 0.25;
quint8 r[] = { 0, 10, 17, 27, 37, 40, 0};
quint8 a[] = { 0,191,255,255,255, 64, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Shift_m0_25()
{
qreal scale = 1.0;
qreal dx = -0.25;
quint8 r[] = { 10, 12, 22, 32, 40, 0, 0};
quint8 a[] = { 64,255,255,255,191, 0, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_0_5_Aligned()
{
qreal scale = 0.5;
qreal dx = 0.0;
quint8 r[] = { 10, 17, 32, 40, 0, 0, 0};
quint8 a[] = { 32,223,223, 32, 0, 0, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_0_5_Shift_0_25()
{
qreal scale = 0.5;
qreal dx = 0.25;
quint8 r[] = { 0, 13, 30, 40, 0, 0, 0};
quint8 a[] = { 0,191,255, 64, 0, 0, 0};
testLine(scale, dx, r, a, -1, 7);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_2_0_Aligned()
{
qreal scale = 2.0;
qreal dx = 0.0;
quint8 r[] = { 0, 10, 10, 12, 17, 22, 27, 32, 37, 40, 40, 0};
quint8 a[] = { 0, 64,191,255,255,255,255,255,255,191, 64, 0};
testLine(scale, dx, r, a, -2, 12);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_2_0_Shift_0_25()
{
qreal scale = 2.0;
qreal dx = 0.25;
quint8 r[] = { 0, 10, 10, 11, 16, 21, 26, 31, 36, 40, 40, 0};
quint8 a[] = { 0, 32,159,255,255,255,255,255,255,223, 96, 0};
testLine(scale, dx, r, a, -2, 12);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_2_0_Shift_0_5()
{
qreal scale = 2.0;
qreal dx = 0.5;
quint8 r[] = { 0, 0, 10, 10, 15, 20, 25, 30, 35, 40, 40, 0};
quint8 a[] = { 0, 0,128,255,255,255,255,255,255,255,127, 0};
testLine(scale, dx, r, a, -2, 12);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Aligned_Clamped()
{
qreal scale = 1.0;
qreal dx = 0.0;
quint8 r[] = { 0, 10, 20, 30, 40, 0, 0};
quint8 a[] = { 0,255,255,255,255, 0, 0};
testLine(scale, dx, r, a, -1, 7, true);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_0_5_Aligned_Clamped()
{
qreal scale = 0.5;
qreal dx = 0.0;
quint8 r[] = { 0, 16, 33, 0, 0, 0, 0};
quint8 a[] = { 0,255,255, 0, 0, 0, 0};
testLine(scale, dx, r, a, -1, 7, true);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_2_0_Aligned_Clamped()
{
qreal scale = 2.0;
qreal dx = 0.0;
quint8 r[] = { 0, 0, 10, 12, 17, 22, 27, 32, 37, 40, 0, 0};
quint8 a[] = { 0, 0,255,255,255,255,255,255,255,255, 0, 0};
testLine(scale, dx, r, a, -2, 12, true);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Aligned_Mirrored()
{
qreal scale = -1.0;
qreal dx = 0.0;
quint8 r[] = { 0, 0, 40, 30, 20, 10, 0, 0, 0, 0};
quint8 a[] = { 0, 0,255,255,255,255, 0, 0, 0, 0};
testLine(scale, dx, r, a, -6, 10);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_1_0_Shift_0_25_Mirrored()
{
qreal scale = -1.0;
qreal dx = 0.25;
quint8 r[] = { 0, 0, 40, 32, 22, 12, 10, 0, 0, 0};
quint8 a[] = { 0, 0,191,255,255,255, 64, 0, 0, 0};
testLine(scale, dx, r, a, -6, 10);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_0_5_Aligned_Mirrored_Clamped()
{
qreal scale = -0.5;
qreal dx = 0.0;
quint8 r[] = { 0, 0, 0, 0, 33, 16, 0, 0, 0, 0};
quint8 a[] = { 0, 0, 0, 0,255,255, 0, 0, 0, 0};
testLine(scale, dx, r, a, -6, 10, true);
}
void KisFilterWeightsApplicatorTest::testProcessLine_Scale_0_5_Shift_0_125_Mirrored()
{
qreal scale = -0.5;
qreal dx = 0.125;
quint8 r[] = { 0, 0, 0, 40, 34, 18, 10, 0, 0, 0};
quint8 a[] = { 0, 0, 0, 16,207,239, 48, 0, 0, 0};
testLine(scale, dx, r, a, -6, 10);
}
void KisFilterWeightsApplicatorTest::benchmarkProcesssLine()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisFilterStrategy *filter = new KisBilinearFilterStrategy();
const qreal scale = 0.873;
const qreal dx = 0.0387;
KisFilterWeightsBuffer buf(filter, qAbs(scale));
KisFilterWeightsApplicator applicator(dev, dev, scale, 0.0, dx, false);
for (int i = 0; i < 32767; i++) {
dev->setPixel(i,0,QColor(10 + i%240,20,40));
}
KisFilterWeightsApplicator::LinePos linePos(0,32767);
QBENCHMARK {
applicator.processLine<KisHLineIteratorSP>(linePos,0,&buf, filter->support());
}
}
QTEST_KDEMAIN(KisFilterWeightsApplicatorTest, GUI)
diff --git a/krita/image/tests/kis_filter_weights_buffer_test.cpp b/krita/image/tests/kis_filter_weights_buffer_test.cpp
index 9fcd1cfcc67..c48d6d01b5f 100644
--- a/krita/image/tests/kis_filter_weights_buffer_test.cpp
+++ b/krita/image/tests/kis_filter_weights_buffer_test.cpp
@@ -1,140 +1,140 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_filter_weights_buffer_test.h"
#include <qtest_kde.h>
#include "kis_filter_strategy.h"
//#define DEBUG_ENABLED
#define SANITY_CHECKS_ENABLED
#include "kis_filter_weights_buffer.h"
#include "kis_debug.h"
void checkWeightsBuffer(KisFilterStrategy *filter, qreal scale)
{
- qDebug() << "Testing:" << filter->name() << "Scale:" << scale;
+ dbgKrita << "Testing:" << filter->name() << "Scale:" << scale;
KisFilterWeightsBuffer buf(filter, scale);
KisFixedPoint fp1;
KisFixedPoint fp2;
const int startIndex = 1;
const int endIndex = 255 * qMin(scale, qreal(1.0));
fp1.from256Frac(startIndex);
fp2.from256Frac(endIndex);
while (fp2 > fp1) {
KisFilterWeightsBuffer::FilterWeights *w1 = buf.weights(fp1);
KisFilterWeightsBuffer::FilterWeights *w2 = buf.weights(fp2);
QCOMPARE(w1->span, w2->span);
int span = w1->span;
// Check for symmetry around center offset
for (int i = 0; i < span; i++) {
int idx2 = span - i - 1;
int v1 = w1->weight[i];
int v2 = w2->weight[idx2];
if (v1 != v2) {
#ifdef DEBUG_ENABLED
- qDebug() << "*******";
- qDebug() << "Weight" << fp1 << "|" << i << ":" << v1;
- qDebug() << "Weight" << fp2 << "|" << idx2 << ":" << v2;
+ dbgKrita << "*******";
+ dbgKrita << "Weight" << fp1 << "|" << i << ":" << v1;
+ dbgKrita << "Weight" << fp2 << "|" << idx2 << ":" << v2;
#endif /* DEBUG_ENABLED */
if (!(span & 0x1) && (qAbs(v1 - v2) <= (0.5 * span))) {
#ifdef DEBUG_ENABLED
- qDebug() << "Symmetry is wrong due to evenly-sized kernel or rounding. It's ok. Accepting.";
+ dbgKrita << "Symmetry is wrong due to evenly-sized kernel or rounding. It's ok. Accepting.";
#endif /* DEBUG_ENABLED */
} else {
QFAIL("Wrong weight symmetry");
}
}
}
fp1.inc256Frac();
fp2.dec256Frac();
}
}
void checkOneFilter(KisFilterStrategy *filter)
{
checkWeightsBuffer(filter, 1.0);
checkWeightsBuffer(filter, 2.0);
checkWeightsBuffer(filter, 0.5);
checkWeightsBuffer(filter, 0.25);
checkWeightsBuffer(filter, 0.125);
delete filter;
checkForAsymmetricZeros = false;
}
void KisFilterWeightsBufferTest::testTriangle()
{
checkForAsymmetricZeros = true;
checkOneFilter(new KisBilinearFilterStrategy());
}
void KisFilterWeightsBufferTest::testHermite()
{
checkOneFilter(new KisHermiteFilterStrategy());
}
void KisFilterWeightsBufferTest::testBicubic()
{
checkOneFilter(new KisBicubicFilterStrategy());
}
void KisFilterWeightsBufferTest::testBox()
{
checkOneFilter(new KisBoxFilterStrategy());
}
void KisFilterWeightsBufferTest::testBell()
{
checkOneFilter(new KisBellFilterStrategy());
}
void KisFilterWeightsBufferTest::testBSpline()
{
checkOneFilter(new KisBSplineFilterStrategy());
}
void KisFilterWeightsBufferTest::testLanczos3()
{
checkOneFilter(new KisLanczos3FilterStrategy());
}
void KisFilterWeightsBufferTest::testMitchell()
{
checkForAsymmetricZeros = true;
checkOneFilter(new KisMitchellFilterStrategy());
}
QTEST_KDEMAIN(KisFilterWeightsBufferTest, GUI)
diff --git a/krita/image/tests/kis_fixed_paint_device_test.cpp b/krita/image/tests/kis_fixed_paint_device_test.cpp
index 12a7602c1b2..0b5b8411fa7 100644
--- a/krita/image/tests/kis_fixed_paint_device_test.cpp
+++ b/krita/image/tests/kis_fixed_paint_device_test.cpp
@@ -1,370 +1,370 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_fixed_paint_device_test.h"
#include <qtest_kde.h>
#include <QTime>
#include <KoColorSpace.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_fixed_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
void KisFixedPaintDeviceTest::testCreation()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev = new KisFixedPaintDevice(cs);
QVERIFY(dev->bounds() == QRect());
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->pixelSize() == cs->pixelSize());
dev->setRect(QRect(0, 0, 100, 100));
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
dev->initialize();
QVERIFY(dev->data() != 0);
quint8* data = dev->data();
for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) {
QVERIFY(data[i] == 0);
}
}
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisFixedPaintDeviceTest::testColorSpaceConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->convertTo(dstCs);
QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height()));
QVERIFY(dev->pixelSize() == dstCs->pixelSize());
QVERIFY(*dev->colorSpace() == *dstCs);
}
void KisFixedPaintDeviceTest::testRoundtripQImageConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->convertFromQImage(image, 0);
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_fixed_paint_device_test_test_roundtrip_qimage.png");
result.save("kis_fixed_paint_device_test_test_roundtrip_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltFixed()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
// Without opacity
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result, 1)) {
fdev->convertToQImage(0).save("kis_fixed_paint_device_test_test_blt_fixed_expected.png");
result.save("kis_fixed_paint_device_test_test_blt_fixed_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltFixedOpacity()
{
// blt a semi-transparent image on a white paint device
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png");
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) {
checkResult.save("kis_fixed_paint_device_test_test_blt_fixed_opactiy_expected.png");
result.save("kis_fixed_paint_device_test_test_blt_fixed_opacity_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testSilly()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->initialize();
dev->initialize();
}
void KisFixedPaintDeviceTest::testClear()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->clear(QRect(0, 0, 100, 100));
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8);
}
void KisFixedPaintDeviceTest::testFill()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
quint8* red = new quint8[cs->pixelSize()];
memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize());
cs->setOpacity(red, quint8(128), 1);
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->fill(0, 0, 100, 100, red);
QVERIFY(dev->bounds() == QRect(0, 0, 100, 100));
QVERIFY(cs->opacityU8(dev->data()) == 128);
QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0);
//Compare fill will normal paint device
dev = new KisFixedPaintDevice(cs);
dev->setRect(QRect(0, 0, 150, 150));
dev->initialize();
dev->fill(50, 50, 50, 50, red);
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->fill(50, 50, 50, 50, red);
QImage image = dev->convertToQImage(0);
QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, checkImage)) {
image.save("kis_fixed_paint_device_filled_result.png");
checkImage.save("kis_fixed_paint_device_filled_result_expected.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
delete[] red;
}
void KisFixedPaintDeviceTest::testBltFixedSmall()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "fixed_blit_small.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
// Without opacity
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 51, 51);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_fixed_paint_device_test_blt_small_image.png");
result.save("kis_fixed_paint_device_test_blt_small_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisFixedPaintDeviceTest::testBltPerformance()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
QTime t;
t.start();
int x;
for (x = 0; x < 1000; ++x) {
KisPainter gc(dev);
gc.bltFixed(QPoint(0, 0), fdev, image.rect());
}
- qDebug() << x
+ dbgKrita << x
<< "blits"
<< " done in "
<< t.elapsed()
<< "ms";
}
inline void setPixel(KisFixedPaintDeviceSP dev, int x, int y, quint8 alpha)
{
KoColor c(Qt::black, dev->colorSpace());
c.setOpacity(alpha);
dev->fill(x, y, 1, 1, c.data());
}
inline quint8 pixel(KisFixedPaintDeviceSP dev, int x, int y)
{
KoColor c(Qt::black, dev->colorSpace());
dev->readBytes(c.data(), x, y, 1, 1);
return c.opacityU8();
}
void KisFixedPaintDeviceTest::testMirroring_data()
{
QTest::addColumn<QRect>("rc");
QTest::addColumn<bool>("mirrorHorizontally");
QTest::addColumn<bool>("mirrorVertically");
QTest::newRow("4, false, false") << (QRect(99,99,4,4)) << false << false;
QTest::newRow("4, false, true") << (QRect(99,99,4,4)) << false << true;
QTest::newRow("4, true, false") << (QRect(99,99,4,4)) << true << false;
QTest::newRow("4, true, true") << (QRect(99,99,4,4)) << true << true;
QTest::newRow("5, false, false") << (QRect(99,99,5,5)) << false << false;
QTest::newRow("5, false, true") << (QRect(99,99,5,5)) << false << true;
QTest::newRow("5, true, false") << (QRect(99,99,5,5)) << true << false;
QTest::newRow("5, true, true") << (QRect(99,99,5,5)) << true << true;
}
void KisFixedPaintDeviceTest::testMirroring()
{
QFETCH(QRect, rc);
QFETCH(bool, mirrorHorizontally);
QFETCH(bool, mirrorVertically);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs);
dev->setRect(rc);
dev->initialize();
KoColor c(Qt::black, cs);
qsrand(1);
int value = 0;
for (int i = rc.x(); i < rc.x() + rc.width(); i++) {
for (int j = rc.y(); j < rc.y() + rc.height(); j++) {
setPixel(dev, i, j, value);
value = qrand() % 255;
}
value = qrand() % 255;
}
//dev->convertToQImage(0).save("0_a.png");
dev->mirror(mirrorHorizontally, mirrorVertically);
//dev->convertToQImage(0).save("0_b.png");
int startX;
int endX;
int incX;
int startY;
int endY;
int incY;
if (mirrorHorizontally) {
startX = rc.x() + rc.width() - 1;
endX = rc.x() - 1;
incX = -1;
} else {
startX = rc.x();
endX = rc.x() + rc.width();
incX = 1;
}
if (mirrorVertically) {
startY = rc.y() + rc.height() - 1;
endY = rc.y() - 1;
incY = -1;
} else {
startY = rc.y();
endY = rc.y() + rc.height();
incY = 1;
}
qsrand(1);
value = 0;
for (int i = startX; i != endX ; i += incX) {
for (int j = startY; j != endY; j += incY) {
QCOMPARE(pixel(dev, i, j), (quint8)value);
value = qrand() % 255;
}
value = qrand() % 255;
}
}
QTEST_KDEMAIN(KisFixedPaintDeviceTest, GUI)
diff --git a/krita/image/tests/kis_gradient_painter_test.cpp b/krita/image/tests/kis_gradient_painter_test.cpp
index 1835bec4aba..71c39e58f94 100644
--- a/krita/image/tests/kis_gradient_painter_test.cpp
+++ b/krita/image/tests/kis_gradient_painter_test.cpp
@@ -1,337 +1,337 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_gradient_painter_test.h"
#include <qtest_kde.h>
#include "kis_gradient_painter.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include "KoStopGradient.h"
#include "krita_utils.h"
#include "testutil.h"
void KisGradientPainterTest::testSimplifyPath()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(202, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
selectionPolygon << QPointF(100, 102);
QPainterPath path;
path.addPolygon(selectionPolygon);
QPainterPath simplifiedPath;
simplifiedPath = KritaUtils::trySimplifyPath(path, 10.0);
QPainterPath ref;
ref.moveTo(100,100);
ref.lineTo(200,100);
ref.lineTo(200,200);
ref.lineTo(100,200);
QCOMPARE(simplifiedPath, ref);
}
void testShapedGradientPainterImpl(const QPolygonF &selectionPolygon,
const QString &testName,
const QPolygonF &selectionErasePolygon = QPolygonF())
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QRect imageRect(0,0,300,300);
KisSelectionSP selection = new KisSelection();
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
KisPainter selPainter(pixelSelection);
selPainter.setFillStyle(KisPainter::FillStyleForegroundColor);
selPainter.setPaintColor(KoColor(Qt::white, pixelSelection->colorSpace()));
selPainter.paintPolygon(selectionPolygon);
if (!selectionErasePolygon.isEmpty()) {
selPainter.setCompositeOp(COMPOSITE_ERASE);
selPainter.setPaintColor(KoColor(Qt::white, pixelSelection->colorSpace()));
selPainter.paintPolygon(selectionErasePolygon);
}
selPainter.end();
pixelSelection->invalidateOutlineCache();
pixelSelection->convertToQImage(0, imageRect).save("sgt_selection.png");
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QScopedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
KisGradientPainter gc(dev, selection);
gc.setGradient(gradient.data());
gc.setGradientShape(KisGradientPainter::GradientShapePolygonal);
gc.paintGradient(selectionPolygon.boundingRect().topLeft(),
selectionPolygon.boundingRect().bottomRight(),
KisGradientPainter::GradientRepeatNone,
0,
false,
imageRect.x(),
imageRect.y(),
imageRect.width(),
imageRect.height());
QVERIFY(TestUtil::checkQImageExternal(dev->convertToQImage(0, imageRect),
"shaped_gradient",
"fill",
testName, 1, 1, 0));
}
void KisGradientPainterTest::testShapedGradientPainterRect()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(202, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
testShapedGradientPainterImpl(selectionPolygon, "rect_shape");
}
void KisGradientPainterTest::testShapedGradientPainterRectPierced()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 100);
selectionPolygon << QPointF(200, 200);
selectionPolygon << QPointF(100, 200);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(150, 150);
selectionErasePolygon << QPointF(155, 150);
selectionErasePolygon << QPointF(155, 155);
selectionErasePolygon << QPointF(150, 155);
testShapedGradientPainterImpl(selectionPolygon, "rect_shape_pierced", selectionErasePolygon);
}
void KisGradientPainterTest::testShapedGradientPainterNonRegular()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
testShapedGradientPainterImpl(selectionPolygon, "nonregular_shape");
}
void KisGradientPainterTest::testShapedGradientPainterNonRegularPierced()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(150, 150);
selectionErasePolygon << QPointF(155, 150);
selectionErasePolygon << QPointF(155, 155);
selectionErasePolygon << QPointF(150, 155);
testShapedGradientPainterImpl(selectionPolygon, "nonregular_shape_pierced", selectionErasePolygon);
}
#include "kis_polygonal_gradient_shape_strategy.h"
void KisGradientPainterTest::testFindShapedExtremums()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPolygonF selectionErasePolygon;
selectionErasePolygon << QPointF(101, 101);
selectionErasePolygon << QPointF(190, 120);
selectionErasePolygon << QPointF(160, 140);
selectionErasePolygon << QPointF(200, 180);
selectionErasePolygon << QPointF(30, 220);
QPainterPath path;
path.addPolygon(selectionPolygon);
path.closeSubpath();
path.addPolygon(selectionErasePolygon);
path.closeSubpath();
QPointF center =
KisPolygonalGradientShapeStrategy::testingCalculatePathCenter(
4, path, 2.0, true);
- qDebug() << ppVar(center);
+ dbgKrita << ppVar(center);
QVERIFY(path.contains(center));
}
#include "krita_utils.h"
void KisGradientPainterTest::testSplitDisjointPaths()
{
QPainterPath path;
// small bug: the smaller rect is also merged
path.addRect(QRectF(323, 123, 4, 4));
path.addRect(QRectF(300, 100, 50, 50));
path.addRect(QRectF(320, 120, 10, 10));
path.addRect(QRectF(200, 100, 50, 50));
path.addRect(QRectF(240, 120, 70, 10));
path.addRect(QRectF(100, 100, 50, 50));
path.addRect(QRectF(120, 120, 10, 10));
path = path.simplified();
{
QImage srcImage(450, 250, QImage::Format_ARGB32);
srcImage.fill(0);
QPainter gc(&srcImage);
gc.fillPath(path, Qt::red);
//srcImage.save("src_disjoint_paths.png");
}
QList<QPainterPath> result = KritaUtils::splitDisjointPaths(path);
{
QImage dstImage(450, 250, QImage::Format_ARGB32);
dstImage.fill(0);
QPainter gc(&dstImage);
QVector<QBrush> brushes;
brushes << Qt::red;
brushes << Qt::green;
brushes << Qt::blue;
brushes << Qt::cyan;
brushes << Qt::magenta;
brushes << Qt::yellow;
brushes << Qt::black;
brushes << Qt::white;
int index = 0;
foreach (const QPainterPath &p, result) {
gc.fillPath(p, brushes[index]);
index = (index + 1) % brushes.size();
}
TestUtil::checkQImageExternal(dstImage,
"shaped_gradient",
"test",
"disjoint_paths");
}
}
#include "kis_polygonal_gradient_shape_strategy.h"
#include "kis_cached_gradient_shape_strategy.h"
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
using namespace boost::accumulators;
void KisGradientPainterTest::testCachedStrategy()
{
QPolygonF selectionPolygon;
selectionPolygon << QPointF(100, 100);
selectionPolygon << QPointF(200, 120);
selectionPolygon << QPointF(170, 140);
selectionPolygon << QPointF(200, 180);
selectionPolygon << QPointF(30, 220);
QPainterPath selectionPath;
selectionPath.addPolygon(selectionPolygon);
QRect rc = selectionPolygon.boundingRect().toAlignedRect();
KisGradientShapeStrategy *strategy =
new KisPolygonalGradientShapeStrategy(selectionPath, 2.0);
KisCachedGradientShapeStrategy cached(rc, 4, 4, strategy);
accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
const qreal maxRelError = 5.0 / 256;
for (int y = rc.y(); y <= rc.bottom(); y++) {
for (int x = rc.x(); x <= rc.right(); x++) {
if (!selectionPolygon.containsPoint(QPointF(x, y), Qt::OddEvenFill)) continue;
qreal ref = strategy->valueAt(x, y);
qreal value = cached.valueAt(x, y);
if (ref == 0.0) continue;
qreal relError = (ref - value)/* / ref*/;
accum(relError);
if (relError > maxRelError) {
- //qDebug() << ppVar(x) << ppVar(y) << ppVar(value) << ppVar(ref) << ppVar(relError);
+ //dbgKrita << ppVar(x) << ppVar(y) << ppVar(value) << ppVar(ref) << ppVar(relError);
}
}
}
- qDebug() << ppVar(count(accum));
- qDebug() << ppVar(mean(accum));
- qDebug() << ppVar(variance(accum));
- qDebug() << ppVar((min)(accum));
- qDebug() << ppVar((max)(accum));
+ dbgKrita << ppVar(count(accum));
+ dbgKrita << ppVar(mean(accum));
+ dbgKrita << ppVar(variance(accum));
+ dbgKrita << ppVar((min)(accum));
+ dbgKrita << ppVar((max)(accum));
qreal varError = variance(accum);
QVERIFY(varError < maxRelError);
qreal maxError = qMax(qAbs((min)(accum)), qAbs((max)(accum)));
QVERIFY(maxError < 2 * maxRelError);
}
QTEST_KDEMAIN(KisGradientPainterTest, GUI)
diff --git a/krita/image/tests/kis_image_test.cpp b/krita/image/tests/kis_image_test.cpp
index 230dd974fa7..0d5d49bf053 100644
--- a/krita/image/tests/kis_image_test.cpp
+++ b/krita/image/tests/kis_image_test.cpp
@@ -1,624 +1,624 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_image_test.h"
#include <QApplication>
#include <qtest_kde.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_selection.h"
#include <kis_debug.h>
#include <kis_layer_composition.h>
#define IMAGE_WIDTH 128
#define IMAGE_HEIGHT 128
void KisImageTest::layerTests()
{
KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
QVERIFY(image->rootLayer() != 0);
QVERIFY(image->rootLayer()->firstChild() == 0);
KisLayerSP layer = new KisPaintLayer(image, "layer 1", OPACITY_OPAQUE_U8);
image->addNode(layer);
QVERIFY(image->rootLayer()->firstChild()->objectName() == layer->objectName());
}
void KisImageTest::testConvertImageColorSpace()
{
const KoColorSpace *cs8 = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs8, "stest");
KisPaintDeviceSP device1 = new KisPaintDevice(cs8);
KisLayerSP paint1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration = filter->defaultConfiguration(0);
Q_ASSERT(configuration);
KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
image->addNode(paint1, image->root());
image->addNode(blur1, image->root());
image->refreshGraph();
const KoColorSpace *cs16 = KoColorSpaceRegistry::instance()->rgb16();
image->lock();
image->convertImageColorSpace(cs16,
KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::InternalConversionFlags);
image->unlock();
QVERIFY(*cs16 == *image->colorSpace());
QVERIFY(*cs16 == *image->root()->colorSpace());
QVERIFY(*cs16 == *paint1->colorSpace());
QVERIFY(*cs16 == *blur1->colorSpace());
QVERIFY(!image->root()->compositeOp());
QVERIFY(*cs16 == *paint1->compositeOp()->colorSpace());
QVERIFY(*cs16 == *blur1->compositeOp()->colorSpace());
image->refreshGraph();
}
void KisImageTest::testGlobalSelection()
{
const KoColorSpace *cs8 = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs8, "stest");
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 0U);
KisSelectionSP selection1 = new KisSelection(new KisDefaultBounds(image));
KisSelectionSP selection2 = new KisSelection(new KisDefaultBounds(image));
image->setGlobalSelection(selection1);
QCOMPARE(image->globalSelection(), selection1);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
image->setGlobalSelection(selection2);
QCOMPARE(image->globalSelection(), selection2);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
image->deselectGlobalSelection();
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), true);
QCOMPARE(image->root()->childCount(), 0U);
image->reselectGlobalSelection();
QCOMPARE(image->globalSelection(), selection2);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
// mixed deselecting/setting/reselecting
image->deselectGlobalSelection();
QCOMPARE(image->globalSelection(), KisSelectionSP(0));
QCOMPARE(image->canReselectGlobalSelection(), true);
QCOMPARE(image->root()->childCount(), 0U);
image->setGlobalSelection(selection1);
QCOMPARE(image->globalSelection(), selection1);
QCOMPARE(image->canReselectGlobalSelection(), false);
QCOMPARE(image->root()->childCount(), 1U);
}
void KisImageTest::testLayerComposition()
{
KisImageSP image = new KisImage(0, IMAGE_WIDTH, IMAGE_WIDTH, 0, "layer tests");
QVERIFY(image->rootLayer() != 0);
QVERIFY(image->rootLayer()->firstChild() == 0);
KisLayerSP layer = new KisPaintLayer(image, "layer 1", OPACITY_OPAQUE_U8);
image->addNode(layer);
KisLayerSP layer2 = new KisPaintLayer(image, "layer 2", OPACITY_OPAQUE_U8);
image->addNode(layer2);
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
KisLayerComposition comp(image, "comp 1");
comp.store();
layer2->setVisible(false);
QVERIFY(layer->visible());
QVERIFY(!layer2->visible());
KisLayerComposition comp2(image, "comp 2");
comp2.store();
comp.apply();
QVERIFY(layer->visible());
QVERIFY(layer2->visible());
comp2.apply();
QVERIFY(layer->visible());
QVERIFY(!layer2->visible());
}
#include "testutil.h"
#include "kis_group_layer.h"
#include "kis_transparency_mask.h"
#include "kis_psd_layer_style.h"
struct FlattenTestImage
{
FlattenTestImage()
: refRect(0,0,512,512)
, p(refRect)
{
image = p.image;
layer1 = p.layer;
layer5 = new KisPaintLayer(p.image, "paint5", 0.4 * OPACITY_OPAQUE_U8);
layer5->disableAlphaChannel(true);
layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8);
tmask = new KisTransparencyMask();
// check channel flags
// make addition composite op
group1 = new KisGroupLayer(p.image, "group1", OPACITY_OPAQUE_U8);
layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8);
layer4 = new KisPaintLayer(p.image, "paint4", OPACITY_OPAQUE_U8);
layer6 = new KisPaintLayer(p.image, "paint6", OPACITY_OPAQUE_U8);
layer7 = new KisPaintLayer(p.image, "paint7", OPACITY_OPAQUE_U8);
layer8 = new KisPaintLayer(p.image, "paint8", OPACITY_OPAQUE_U8);
layer7->setCompositeOp(COMPOSITE_ADD);
layer8->setCompositeOp(COMPOSITE_ADD);
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect tmaskRect(200,200,100,100);
QRect rect3(400, 100, 100, 100);
QRect rect4(500, 100, 100, 100);
QRect rect5(50, 50, 100, 100);
QRect rect6(50, 250, 100, 100);
QRect rect7(50, 350, 50, 50);
QRect rect8(50, 400, 50, 50);
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, p.image->colorSpace()));
layer2->paintDevice()->fill(rect2, KoColor(Qt::green, p.image->colorSpace()));
tmask->testingInitSelection(tmaskRect);
layer3->paintDevice()->fill(rect3, KoColor(Qt::blue, p.image->colorSpace()));
layer4->paintDevice()->fill(rect4, KoColor(Qt::yellow, p.image->colorSpace()));
layer5->paintDevice()->fill(rect5, KoColor(Qt::green, p.image->colorSpace()));
layer6->paintDevice()->fill(rect6, KoColor(Qt::cyan, p.image->colorSpace()));
layer7->paintDevice()->fill(rect7, KoColor(Qt::red, p.image->colorSpace()));
layer8->paintDevice()->fill(rect8, KoColor(Qt::green, p.image->colorSpace()));
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setEffectEnabled(true);
style->dropShadow()->setDistance(10.0);
style->dropShadow()->setSpread(80.0);
style->dropShadow()->setSize(10);
style->dropShadow()->setNoise(0);
style->dropShadow()->setKnocksOut(false);
style->dropShadow()->setOpacity(80.0);
layer2->setLayerStyle(style);
layer2->setCompositeOp(COMPOSITE_ADD);
group1->setCompositeOp(COMPOSITE_ADD);
p.image->addNode(layer5);
p.image->addNode(layer2);
p.image->addNode(tmask, layer2);
p.image->addNode(group1);
p.image->addNode(layer3, group1);
p.image->addNode(layer4, group1);
p.image->addNode(layer6);
p.image->addNode(layer7);
p.image->addNode(layer8);
p.image->initialRefreshGraph();
- // qDebug() << ppVar(layer1->exactBounds());
- // qDebug() << ppVar(layer5->exactBounds());
- // qDebug() << ppVar(layer2->exactBounds());
- // qDebug() << ppVar(group1->exactBounds());
- // qDebug() << ppVar(layer3->exactBounds());
- // qDebug() << ppVar(layer4->exactBounds());
+ // dbgKrita << ppVar(layer1->exactBounds());
+ // dbgKrita << ppVar(layer5->exactBounds());
+ // dbgKrita << ppVar(layer2->exactBounds());
+ // dbgKrita << ppVar(group1->exactBounds());
+ // dbgKrita << ppVar(layer3->exactBounds());
+ // dbgKrita << ppVar(layer4->exactBounds());
TestUtil::ExternalImageChecker chk("flatten", "imagetest");
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
}
QRect refRect;
TestUtil::MaskParent p;
KisImageSP image;
KisPaintLayerSP layer1;
KisPaintLayerSP layer2;
KisTransparencyMaskSP tmask;
KisGroupLayerSP group1;
KisPaintLayerSP layer3;
KisPaintLayerSP layer4;
KisPaintLayerSP layer5;
KisPaintLayerSP layer6;
KisPaintLayerSP layer7;
KisPaintLayerSP layer8;
};
void KisImageTest::testFlattenLayer()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker chk("flatten", "imagetest");
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
KisLayerSP newLayer = p.image->flattenLayer(p.layer2);
p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
}
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
KisLayerSP newLayer = p.image->flattenLayer(p.group1);
p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "02_group1_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(newLayer->exactBounds(), QRect(400, 100, 200, 100));
}
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = p.image->flattenLayer(p.layer5);
p.image->waitForDone();
QVERIFY(chk.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "03_layer5_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 50, 100, 100));
QCOMPARE(newLayer->alphaChannelDisabled(), true);
}
}
#include <metadata/kis_meta_data_merge_strategy_registry.h>
void KisImageTest::testMergeDown()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_simple", "imagetest");
{
QCOMPARE(p.layer5->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer5->alphaChannelDisabled(), true);
KisLayerSP newLayer = p.image->mergeDown(p.layer5, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer5_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.layer2, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "02_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(100, 100, 213, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.group1, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "03_group1_mergedown_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(100, 100, 500, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationInheritsAlpha()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_dst_inheritsalpha", "imagetest");
{
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.layer2, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer2_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50,50, 263, 267));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationCustomCompositeOp()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_dst_customop", "imagetest");
{
QCOMPARE(p.layer6->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(p.layer6->alphaChannelDisabled(), false);
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.layer6, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer6_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationSameCompositeOpLayerStyle()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_sameop_ls", "imagetest");
{
QCOMPARE(p.group1->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.group1->alphaChannelDisabled(), false);
QCOMPARE(p.layer2->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer2->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.group1, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_group1_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(197, 100, 403, 217));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeDownDestinationSameCompositeOp()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergedown_sameop_fastpath", "imagetest");
{
QCOMPARE(p.layer8->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer8->alphaChannelDisabled(), false);
QCOMPARE(p.layer7->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(p.layer7->alphaChannelDisabled(), false);
KisLayerSP newLayer = p.image->mergeDown(p.layer8, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_ADD);
QCOMPARE(newLayer->exactBounds(), QRect(50, 350, 50, 100));
QCOMPARE(newLayer->alphaChannelDisabled(), false);
}
}
void KisImageTest::testMergeMultiple()
{
FlattenTestImage p;
TestUtil::ExternalImageChecker img("flatten", "imagetest");
TestUtil::ExternalImageChecker chk("mergemultiple", "imagetest");
{
QList<KisNodeSP> selectedNodes;
selectedNodes << p.layer2
<< p.group1
<< p.layer6;
{
KisNodeSP newLayer = p.image->mergeMultipleLayers(selectedNodes, 0);
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
}
}
p.p.undoStore->undo();
p.image->waitForDone();
// Test reversed order, the result must be the same
{
QList<KisNodeSP> selectedNodes;
selectedNodes << p.layer6
<< p.group1
<< p.layer2;
{
KisNodeSP newLayer = p.image->mergeMultipleLayers(selectedNodes, 0);
p.image->waitForDone();
QVERIFY(img.checkDevice(p.image->projection(), p.image, "00_initial"));
QVERIFY(chk.checkDevice(newLayer->projection(), p.image, "01_layer8_layerproj"));
QCOMPARE(newLayer->compositeOpId(), COMPOSITE_OVER);
QCOMPARE(newLayer->exactBounds(), QRect(50, 100, 550, 250));
}
}
}
void testMergeCrossColorSpaceImpl(bool useProjectionColorSpace, bool swapSpaces)
{
QRect refRect;
TestUtil::MaskParent p;
KisPaintLayerSP layer1;
KisPaintLayerSP layer2;
KisPaintLayerSP layer3;
const KoColorSpace *cs2 = useProjectionColorSpace ?
p.image->colorSpace() : KoColorSpaceRegistry::instance()->lab16();
const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();
if (swapSpaces) {
qSwap(cs2, cs3);
}
- qDebug() << "Testing testMergeCrossColorSpaceImpl:";
- qDebug() << " " << ppVar(cs2);
- qDebug() << " " << ppVar(cs3);
+ dbgKrita << "Testing testMergeCrossColorSpaceImpl:";
+ dbgKrita << " " << ppVar(cs2);
+ dbgKrita << " " << ppVar(cs3);
layer1 = p.layer;
layer2 = new KisPaintLayer(p.image, "paint2", OPACITY_OPAQUE_U8, cs2);
layer3 = new KisPaintLayer(p.image, "paint3", OPACITY_OPAQUE_U8, cs3);
QRect rect1(100, 100, 100, 100);
QRect rect2(150, 150, 150, 150);
QRect rect3(250, 250, 200, 200);
layer1->paintDevice()->fill(rect1, KoColor(Qt::red, layer1->colorSpace()));
layer2->paintDevice()->fill(rect2, KoColor(Qt::green, layer2->colorSpace()));
layer3->paintDevice()->fill(rect3, KoColor(Qt::blue, layer3->colorSpace()));
p.image->addNode(layer2);
p.image->addNode(layer3);
p.image->initialRefreshGraph();
{
KisLayerSP newLayer = p.image->mergeDown(layer3, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
}
{
layer2->disableAlphaChannel(true);
KisLayerSP newLayer = p.image->mergeDown(layer3, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
p.image->waitForDone();
QCOMPARE(newLayer->colorSpace(), p.image->colorSpace());
p.undoStore->undo();
}
}
void KisImageTest::testMergeCrossColorSpace()
{
testMergeCrossColorSpaceImpl(true, false);
testMergeCrossColorSpaceImpl(true, true);
testMergeCrossColorSpaceImpl(false, false);
testMergeCrossColorSpaceImpl(false, true);
}
QTEST_KDEMAIN(KisImageTest, NoGUI)
diff --git a/krita/image/tests/kis_iterator_benchmark.cpp b/krita/image/tests/kis_iterator_benchmark.cpp
index af57f7d4a91..442dd13fe8f 100644
--- a/krita/image/tests/kis_iterator_benchmark.cpp
+++ b/krita/image/tests/kis_iterator_benchmark.cpp
@@ -1,235 +1,235 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_iterator_benchmark.h"
#include <QApplication>
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include "kis_random_accessor_ng.h"
#include "kis_random_sub_accessor.h"
#include <kis_iterator_ng.h>
#include <kis_repeat_iterators_pixel.h>
#include "kis_paint_device.h"
#define TEST_WIDTH 3000
#define TEST_HEIGHT 3000
void KisIteratorBenchmark::sequentialIter(const KoColorSpace * colorSpace)
{
KisPaintDeviceSP dev = new KisPaintDevice(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 64*64];
memset(bytes, 128, 64 * 64 * colorSpace->pixelSize());
QTime t;
t.start();
for (int i = 0; i < 3; i++) {
KisSequentialIterator it(dev, QRect(0, 0, TEST_WIDTH, TEST_HEIGHT));
do {
memcpy(it.rawData(), bytes, colorSpace->pixelSize());
} while (it.nextPixel());
- qDebug() << "SequentialIterator run " << i << "took" << t.elapsed();
+ dbgKrita << "SequentialIterator run " << i << "took" << t.elapsed();
t.restart();
}
t.restart();
for (int i = 0; i < 3; i++) {
KisSequentialConstIterator it(dev, QRect(0, 0, TEST_WIDTH, TEST_HEIGHT));
do {
//memcpy(it.rawData(), bytes, colorSpace->pixelSize());
} while (it.nextPixel());
- qDebug() << "SequentialConstIterator run " << i << "took" << t.elapsed();
+ dbgKrita << "SequentialConstIterator run " << i << "took" << t.elapsed();
t.restart();
}
delete[] bytes;
}
void KisIteratorBenchmark::hLineIterNG(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 128];
memset(bytes, 128, 128 * colorSpace->pixelSize());
QTime t;
t.start();
for (int i = 0; i < 3; i++) {
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, TEST_WIDTH);
for (int j = 0; j < TEST_HEIGHT; j++) {
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
it->nextRow();
}
- qDebug() << "HLineIteratorNG run " << i << "took" << t.elapsed();
+ dbgKrita << "HLineIteratorNG run " << i << "took" << t.elapsed();
t.restart();
}
for (int i = 0; i < 3; i++) {
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, TEST_WIDTH);
for (int j = 0; j < TEST_HEIGHT; j++) {
int pixels;
do {
pixels = it->nConseqPixels();
memcpy(it->rawData(), bytes, pixels * colorSpace->pixelSize());
} while (it->nextPixels(pixels));
it->nextRow();
}
- qDebug() << "HLineIteratorNG with nConseqHPixels run " << i << "took" << t.elapsed();
+ dbgKrita << "HLineIteratorNG with nConseqHPixels run " << i << "took" << t.elapsed();
t.restart();
}
KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, TEST_WIDTH);
for (int i = 0; i < TEST_HEIGHT; i++) {
do {
//do stuff
} while (cit->nextPixel());
cit->nextRow();
}
- qDebug() << "const HLineIteratorNG took" << t.elapsed();
+ dbgKrita << "const HLineIteratorNG took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::vLineIterNG(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize()];
memset(bytes, 128, colorSpace->pixelSize());
QTime t;
t.start();
for (int i = 0; i < 3; i++) {
KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, TEST_HEIGHT);
for (int j = 0; j < TEST_WIDTH; j++) {
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while(it->nextPixel());
it->nextColumn();
}
- qDebug() << "VLineIteratorNG run " << i << " took" << t.elapsed();
+ dbgKrita << "VLineIteratorNG run " << i << " took" << t.elapsed();
t.restart();
}
KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, TEST_HEIGHT);
for (int i = 0; i < TEST_WIDTH; i++) {
do {} while(cit->nextPixel());
cit->nextColumn();
}
- qDebug() << "const VLineIteratorNG took" << t.elapsed();
+ dbgKrita << "const VLineIteratorNG took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::randomAccessor(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = new quint8[colorSpace->pixelSize() * 128];
memset(bytes, 128, 128 * colorSpace->pixelSize());
QTime t;
t.start();
for (int i = 0; i < 3; i++) {
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ++y) {
for (int x = 0; x < TEST_WIDTH; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
- qDebug() << "RandomIterator run " << i << " took" << t.elapsed();
+ dbgKrita << "RandomIterator run " << i << " took" << t.elapsed();
t.restart();
}
for (int i = 0; i < 3; i++) {
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ) {
int numContiguousRows = qMin(ac->numContiguousRows(y), TEST_HEIGHT - y);
for (int x = 0; x < TEST_WIDTH; ) {
int numContiguousColumns = qMin(ac->numContiguousColumns(x), TEST_WIDTH - x);
ac->moveTo(x, y);
int rowStride = ac->rowStride(x, y);
quint8 *data = ac->rawData();
for (int i = 0; i < numContiguousRows; i++) {
memcpy(data, bytes, numContiguousColumns * colorSpace->pixelSize());
data += rowStride;
}
x += numContiguousColumns;
}
y += numContiguousRows;
}
- qDebug() << "RandomIterator run (with strides)" << i << " took" << t.elapsed();
+ dbgKrita << "RandomIterator run (with strides)" << i << " took" << t.elapsed();
t.restart();
}
KisRandomConstAccessorSP cac = dev.createRandomConstAccessorNG(0, 0);
for (int y = 0; y < TEST_HEIGHT; ++y) {
for (int x = 0; x < TEST_WIDTH; ++x) {
cac->moveTo(x, y);
}
}
- qDebug() << "const RandomIterator took" << t.elapsed();
+ dbgKrita << "const RandomIterator took" << t.elapsed();
delete[] bytes;
}
void KisIteratorBenchmark::runBenchmark()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
hLineIterNG(cs);
vLineIterNG(cs);
sequentialIter(cs);
randomAccessor(cs);
}
QTEST_KDEMAIN(KisIteratorBenchmark, NoGUI)
diff --git a/krita/image/tests/kis_iterator_test.cpp b/krita/image/tests/kis_iterator_test.cpp
index 1d6d366088b..642c503013a 100644
--- a/krita/image/tests/kis_iterator_test.cpp
+++ b/krita/image/tests/kis_iterator_test.cpp
@@ -1,486 +1,486 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_iterator_test.h"
#include <QApplication>
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColor.h>
#include "kis_random_accessor_ng.h"
#include "kis_random_sub_accessor.h"
#include <kis_iterator_ng.h>
#include "kis_paint_device.h"
#include <kis_repeat_iterators_pixel.h>
void KisIteratorTest::allCsApplicator(void (KisIteratorTest::* funcPtr)(const KoColorSpace*cs))
{
QList<const KoColorSpace*> colorsapces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
foreach(const KoColorSpace* cs, colorsapces) {
- qDebug() << "Testing with" << cs->id();
+ dbgKrita << "Testing with" << cs->id();
if (cs->id() != "GRAYU16") // No point in testing extend for GRAYU16
(this->*funcPtr)(cs);
}
}
inline quint8* allocatePixels(const KoColorSpace *colorSpace, int numPixels)
{
quint8 *bytes = new quint8[colorSpace->pixelSize() * 64 * 64 * 10];
KoColor color(Qt::red, colorSpace);
const int pixelSize = colorSpace->pixelSize();
for(int i = 0; i < numPixels; i++) {
memcpy(bytes + i * pixelSize, color.data(), pixelSize);
}
return bytes;
}
void KisIteratorTest::writeBytes(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
// Check allocation on tile boundaries
// Allocate memory for a 2 * 5 tiles grid
quint8* bytes = allocatePixels(colorSpace, 64 * 64 * 10);
// Covers 5 x 2 tiles
dev.writeBytes(bytes, 0, 0, 5 * 64, 2 * 64);
// Covers
QCOMPARE(dev.extent(), QRect(0, 0, 64 * 5, 64 * 2));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 64 * 5, 64 * 2));
dev.clear();
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
dev.clear();
// Covers three by three tiles
dev.writeBytes(bytes, 10, 10, 130, 130);
QCOMPARE(dev.extent(), QRect(0, 0, 64 * 3, 64 * 3));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 130, 130));
dev.clear();
// Covers 11 x 2 tiles
dev.writeBytes(bytes, -10, -10, 10 * 64, 64);
QCOMPARE(dev.extent(), QRect(-64, -64, 64 * 11, 64 * 2));
QCOMPARE(dev.exactBounds(), QRect(-10, -10, 640, 64));
delete[] bytes;
}
void KisIteratorTest::fill(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
quint8 * bytes = allocatePixels(colorSpace, 1);
dev.fill(0, 0, 5, 5, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 64, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 5, 5));
dev.clear();
dev.fill(5, 5, 5, 5, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 64, 64));
QCOMPARE(dev.exactBounds(), QRect(5, 5, 5, 5));
dev.clear();
dev.fill(5, 5, 500, 500, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 8 * 64, 8 * 64));
QCOMPARE(dev.exactBounds(), QRect(5, 5, 500, 500));
dev.clear();
dev.fill(33, -10, 348, 1028, bytes);
QCOMPARE(dev.extent(), QRect(0, -64, 6 * 64, 17 * 64));
QCOMPARE(dev.exactBounds(), QRect(33, -10, 348, 1028));
delete[] bytes;
}
void KisIteratorTest::hLineIter(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, 128);
do {} while (cit->nextPixel());
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1)));
{
dev.clear();
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 1));
}
dev.clear();
{
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 1, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 1, 128, 1));
}
dev.clear();
{
KisHLineIteratorSP it = dev.createHLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 192, 64));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1));
}
dev.clear();
dev.setX(10);
dev.setY(-15);
{
KisHLineIteratorSP it = dev.createHLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(10, -15, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1));
}
delete[] bytes;
}
void KisIteratorTest::vLineIter(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, 128);
do {} while (cit->nextPixel());
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1)));
{
KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE((QRect) dev.extent(), QRect(0, 0, 64, 128));
QCOMPARE((QRect) dev.exactBounds(), QRect(0, 0, 1, 128));
}
dev.clear();
{
KisVLineIteratorSP it = dev.createVLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 64, 192));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128));
}
dev.clear();
dev.setX(10);
dev.setY(-15);
{
KisVLineIteratorSP it = dev.createVLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(10, -15, 64, 192));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128));
}
delete[] bytes;
}
void KisIteratorTest::randomAccessor(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisRandomConstAccessorSP acc = dev.createRandomConstAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
acc->moveTo(x, y);
}
}
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
QCOMPARE(dev.extent(), QRect(0, 0, 128, 128));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128));
dev.clear();
dev.setX(10);
dev.setY(-15);
{
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
QCOMPARE(dev.extent(), QRect(-54, -15, 192, 192));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128));
}
delete[] bytes;
}
void KisIteratorTest::repeatHLineIter(const KoColorSpace* cs)
{
KoColor color(Qt::green, cs);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(5, 5, 10, 10, color.data());
KisRepeatHLineConstIteratorSP iter = dev->createRepeatHLineConstIterator(0, 0, 20, QRect(5, 5, 10, 10));
for(int i = 0; i < 20; i++) {
do {
QVERIFY(!memcmp(color.data(), iter->oldRawData(), cs->pixelSize()));
} while (iter->nextPixel());
iter->nextRow();
}
}
void KisIteratorTest::repeatVLineIter(const KoColorSpace* cs)
{
KoColor color(Qt::green, cs);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(5, 5, 10, 10, color.data());
KisRepeatVLineConstIteratorSP iter = dev->createRepeatVLineConstIterator(0, 0, 20, QRect(5, 5, 10, 10));
for(int i = 0; i < 20; i++) {
do {
QVERIFY(!memcmp(color.data(), iter->oldRawData(), cs->pixelSize()));
} while (iter->nextPixel());
iter->nextColumn();
}
}
void KisIteratorTest::writeBytes()
{
allCsApplicator(&KisIteratorTest::writeBytes);
}
void KisIteratorTest::fill()
{
allCsApplicator(&KisIteratorTest::fill);
}
void KisIteratorTest::hLineIter()
{
allCsApplicator(&KisIteratorTest::hLineIter);
}
void KisIteratorTest::vLineIter()
{
allCsApplicator(&KisIteratorTest::vLineIter);
}
void KisIteratorTest::randomAccessor()
{
allCsApplicator(&KisIteratorTest::randomAccessor);
}
void KisIteratorTest::repeatHLineIter()
{
allCsApplicator(&KisIteratorTest::repeatHLineIter);
}
void KisIteratorTest::repeatVLineIter()
{
allCsApplicator(&KisIteratorTest::repeatVLineIter);
}
#define NUM_CYCLES 10000
#define NUM_THREADS 10
class DataReaderThread : public QRunnable {
public:
DataReaderThread(KisPaintDeviceSP device, const QRect &rect)
: m_device(device),
m_rect(rect)
{}
void run() {
for(int i = 0; i < NUM_CYCLES; i++) {
KisRandomAccessorSP iter =
m_device->createRandomAccessorNG(m_rect.x(), m_rect.y());
qint32 rowsRemaining = m_rect.height();
qint32 y = m_rect.y();
while (rowsRemaining > 0) {
qint32 columnsRemaining = m_rect.width();
qint32 x = m_rect.x();
qint32 numContiguousRows = iter->numContiguousRows(y);
qint32 rows = qMin(numContiguousRows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousColumns = iter->numContiguousColumns(x);
qint32 columns = qMin(numContiguousColumns, columnsRemaining);
qint32 rowStride = iter->rowStride(x, y);
iter->moveTo(x, y);
- // qDebug() << "BitBlt:" << ppVar(x) << ppVar(y)
+ // dbgKrita << "BitBlt:" << ppVar(x) << ppVar(y)
// << ppVar(columns) << ppVar(rows)
// << ppVar(rowStride);
doBitBlt(iter->rawData(), rowStride, m_device->pixelSize(),
rows, columns);
x += columns;
columnsRemaining -= columns;
}
y += rows;
rowsRemaining -= rows;
}
}
}
private:
void doBitBltConst(const quint8* data, qint32 rowStride, qint32 pixelSize,
qint32 rows, qint32 columns) {
for(int i = 0; i < rows; i++) {
Q_ASSERT(columns * pixelSize < 256);
quint8 tempData[256];
memcpy(tempData, data, columns * pixelSize);
data += rowStride;
}
}
void doBitBlt(quint8* data, qint32 rowStride, qint32 pixelSize,
qint32 rows, qint32 columns) {
for(int i = 0; i < rows; i++) {
// Let's write something here...
memset(data, 0x13, columns * pixelSize);
data += rowStride;
}
}
private:
KisPaintDeviceSP m_device;
QRect m_rect;
};
class NastyThread : public QRunnable {
public:
NastyThread(KisPaintDeviceSP device)
: m_device(device)
{}
void run() {
for(int i = 0; i < NUM_CYCLES; i++) {
m_device->setX(-0x400 + (qrand() & 0x7FF));
m_device->setY(-0x400 + (qrand() & 0x7FF));
QTest::qSleep(10);
}
}
private:
KisPaintDeviceSP m_device;
};
void KisIteratorTest::stressTest()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
QRect imageRect(0,0,2000,2000);
KisPaintDeviceSP device = new KisPaintDevice(colorSpace);
device->fill(imageRect, KoColor(Qt::red, colorSpace));
QThreadPool threadPool;
threadPool.setMaxThreadCount(NUM_THREADS);
for(int i = 0; i< NUM_THREADS; i++) {
QRect rc = QRect(double(i) / NUM_THREADS * 2000, 0, 2000 / NUM_THREADS, 2000);
-// qDebug() << rc;
+// dbgKrita << rc;
DataReaderThread *reader = new DataReaderThread(device, rc);
threadPool.start(reader);
if(!(i & 0x1)) {
NastyThread *nasty = new NastyThread(device);
threadPool.start(nasty);
}
}
threadPool.waitForDone();
}
QTEST_KDEMAIN(KisIteratorTest, NoGUI)
diff --git a/krita/image/tests/kis_iterators_ng_test.cpp b/krita/image/tests/kis_iterators_ng_test.cpp
index af15fbe1bc3..9d08b760584 100644
--- a/krita/image/tests/kis_iterators_ng_test.cpp
+++ b/krita/image/tests/kis_iterators_ng_test.cpp
@@ -1,421 +1,421 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_iterators_ng_test.h"
#include <QApplication>
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include "kis_random_accessor_ng.h"
#include "kis_random_sub_accessor.h"
#include "kis_paint_device.h"
#include <kis_iterator_ng.h>
void KisIteratorTest::allCsApplicator(void (KisIteratorTest::* funcPtr)(const KoColorSpace*cs))
{
QList<const KoColorSpace*> colorsapces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
foreach(const KoColorSpace* cs, colorsapces) {
- qDebug() << "Testing with" << cs->id();
+ dbgKrita << "Testing with" << cs->id();
if (cs->id() != "GRAYU16") // No point in testing extend for GRAYU16
(this->*funcPtr)(cs);
}
}
inline quint8* allocatePixels(const KoColorSpace *colorSpace, int numPixels)
{
quint8 * bytes = new quint8[colorSpace->pixelSize() * 64 * 64 * 10];
KoColor color(Qt::red, colorSpace);
const int pixelSize = colorSpace->pixelSize();
for(int i = 0; i < numPixels; i++) {
memcpy(bytes + i * pixelSize, color.data(), pixelSize);
}
return bytes;
}
void KisIteratorTest::writeBytes(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
// Check allocation on tile boundaries
// Allocate memory for a 2 * 5 tiles grid
quint8* bytes = allocatePixels(colorSpace, 64 * 64 * 10);
// Covers 5 x 2 tiles
dev.writeBytes(bytes, 0, 0, 5 * 64, 2 * 64);
// Covers
QCOMPARE(dev.extent(), QRect(0, 0, 64 * 5, 64 * 2));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 64 * 5, 64 * 2));
dev.clear();
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
dev.clear();
// Covers three by three tiles
dev.writeBytes(bytes, 10, 10, 130, 130);
QCOMPARE(dev.extent(), QRect(0, 0, 64 * 3, 64 * 3));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 130, 130));
dev.clear();
// Covers 11 x 2 tiles
dev.writeBytes(bytes, -10, -10, 10 * 64, 64);
QCOMPARE(dev.extent(), QRect(-64, -64, 64 * 11, 64 * 2));
QCOMPARE(dev.exactBounds(), QRect(-10, -10, 640, 64));
delete[] bytes;
}
void KisIteratorTest::fill(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
quint8 * bytes = allocatePixels(colorSpace, 1);
dev.fill(0, 0, 5, 5, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 64, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 5, 5));
dev.clear();
dev.fill(5, 5, 5, 5, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 64, 64));
QCOMPARE(dev.exactBounds(), QRect(5, 5, 5, 5));
dev.clear();
dev.fill(5, 5, 500, 500, bytes);
QCOMPARE(dev.extent(), QRect(0, 0, 8 * 64, 8 * 64));
QCOMPARE(dev.exactBounds(), QRect(5, 5, 500, 500));
dev.clear();
dev.fill(33, -10, 348, 1028, bytes);
QCOMPARE(dev.extent(), QRect(0, -64, 6 * 64, 17 * 64));
QCOMPARE(dev.exactBounds(), QRect(33, -10, 348, 1028));
delete[] bytes;
}
void KisIteratorTest::sequentialIter(const KoColorSpace * colorSpace)
{
KisPaintDeviceSP dev = new KisPaintDevice(colorSpace);
QCOMPARE(dev->extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
// Const does not extend the extent
{
KisSequentialConstIterator it(dev, QRect(0, 0, 128, 128));
while (it.nextPixel());
QCOMPARE(dev->extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
QCOMPARE(dev->exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1)));
}
// Non-const does
{
KisSequentialIterator it(dev, QRect(0, 0, 128, 128));
int i = -1;
do {
i++;
KoColor c(QColor(i % 255, i / 255, 0), colorSpace);
memcpy(it.rawData(), c.data(), colorSpace->pixelSize());
QCOMPARE(it.x(), i % 128);
QCOMPARE(it.y(), i / 128);
} while (it.nextPixel());
QCOMPARE(dev->extent(), QRect(0, 0, 128, 128));
QCOMPARE(dev->exactBounds(), QRect(0, 0, 128, 128));
}
{ // check const iterator
KisSequentialConstIterator it(dev, QRect(0, 0, 128, 128));
int i = -1;
do {
i++;
KoColor c(QColor(i % 255, i / 255, 0), colorSpace);
QVERIFY(memcmp(it.rawDataConst(), c.data(), colorSpace->pixelSize()) == 0);
} while (it.nextPixel());
QCOMPARE(dev->extent(), QRect(0, 0, 128, 128));
QCOMPARE(dev->exactBounds(), QRect(0, 0, 128, 128));
}
dev->clear();
{
KisSequentialIterator it(dev, QRect(10, 10, 128, 128));
int i = -1;
do {
i++;
KoColor c(QColor(i % 255, i / 255, 0), colorSpace);
memcpy(it.rawData(), c.data(), colorSpace->pixelSize());
} while (it.nextPixel());
QCOMPARE(dev->extent(), QRect(0, 0, 3 * 64, 3 * 64));
QCOMPARE(dev->exactBounds(), QRect(10, 10, 128, 128));
}
dev->clear();
dev->setX(10);
dev->setY(-15);
{
KisSequentialIterator it(dev, QRect(10, 10, 128, 128));
int i = -1;
do {
i++;
KoColor c(QColor(i % 255, i / 255, 0), colorSpace);
memcpy(it.rawData(), c.data(), colorSpace->pixelSize());
} while (it.nextPixel());
QCOMPARE(dev->extent(), QRect(10, -15, 128, 192));
QCOMPARE(dev->exactBounds(), QRect(10, 10, 128, 128));
}
{
KisSequentialIterator it(dev, QRect(10, 10, 128, 128));
QCOMPARE(it.rawData(), it.oldRawData());
}
}
void KisIteratorTest::hLineIter(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, 128);
while (!cit->nextPixel());
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1)));
dev.clear();
KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 1));
dev.clear();
it = dev.createHLineIteratorNG(0, 1, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(0, 1, 128, 1));
dev.clear();
it = dev.createHLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 192, 64));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1));
dev.clear();
dev.setX(10);
dev.setY(-15);
it = dev.createHLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while (it->nextPixel());
QCOMPARE(dev.extent(), QRect(10, -15, 128, 64));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1));
it = dev.createHLineIteratorNG(10, 10, 128);
it->nextRow();
QCOMPARE(it->rawData(), it->oldRawData());
delete[] bytes;
}
void KisIteratorTest::justCreation(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
dev.createVLineConstIteratorNG(0, 0, 128);
dev.createVLineIteratorNG(0, 0, 128);
dev.createHLineConstIteratorNG(0, 0, 128);
dev.createHLineIteratorNG(0, 0, 128);
}
void KisIteratorTest::vLineIter(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, 128);
while (cit->nextPixel());
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1)));
cit.clear();
KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while(it->nextPixel());
QCOMPARE((QRect) dev.extent(), QRect(0, 0, 64, 128));
QCOMPARE((QRect) dev.exactBounds(), QRect(0, 0, 1, 128));
it.clear();
dev.clear();
it = dev.createVLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while(it->nextPixel());
QCOMPARE(dev.extent(), QRect(0, 0, 64, 192));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128));
dev.clear();
dev.setX(10);
dev.setY(-15);
it = dev.createVLineIteratorNG(10, 10, 128);
do {
memcpy(it->rawData(), bytes, colorSpace->pixelSize());
} while(it->nextPixel());
QCOMPARE(dev.extent(), QRect(10, -15, 64, 192));
QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128));
it = dev.createVLineIteratorNG(10, 10, 128);
it->nextColumn();
QCOMPARE(it->rawData(), it->oldRawData());
delete[] bytes;
}
void KisIteratorTest::randomAccessor(const KoColorSpace * colorSpace)
{
KisPaintDevice dev(colorSpace);
quint8 * bytes = allocatePixels(colorSpace, 1);
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisRandomConstAccessorSP acc = dev.createRandomConstAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
acc->moveTo(x, y);
}
}
QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0));
KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
QCOMPARE(dev.extent(), QRect(0, 0, 128, 128));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128));
dev.clear();
dev.setX(10);
dev.setY(-15);
ac = dev.createRandomAccessorNG(0, 0);
for (int y = 0; y < 128; ++y) {
for (int x = 0; x < 128; ++x) {
ac->moveTo(x, y);
memcpy(ac->rawData(), bytes, colorSpace->pixelSize());
}
}
QCOMPARE(dev.extent(), QRect(-54, -15, 192, 192));
QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128));
delete[] bytes;
}
void KisIteratorTest::writeBytes()
{
allCsApplicator(&KisIteratorTest::writeBytes);
}
void KisIteratorTest::fill()
{
allCsApplicator(&KisIteratorTest::fill);
}
void KisIteratorTest::sequentialIter()
{
allCsApplicator(&KisIteratorTest::sequentialIter);
}
void KisIteratorTest::hLineIter()
{
allCsApplicator(&KisIteratorTest::hLineIter);
}
void KisIteratorTest::justCreation()
{
allCsApplicator(&KisIteratorTest::justCreation);
}
void KisIteratorTest::vLineIter()
{
allCsApplicator(&KisIteratorTest::vLineIter);
}
void KisIteratorTest::randomAccessor()
{
allCsApplicator(&KisIteratorTest::randomAccessor);
}
QTEST_KDEMAIN(KisIteratorTest, NoGUI)
diff --git a/krita/image/tests/kis_layer_style_projection_plane_test.cpp b/krita/image/tests/kis_layer_style_projection_plane_test.cpp
index 1222c4ab639..4ab6281274f 100644
--- a/krita/image/tests/kis_layer_style_projection_plane_test.cpp
+++ b/krita/image/tests/kis_layer_style_projection_plane_test.cpp
@@ -1,500 +1,500 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_style_projection_plane_test.h"
#include <qtest_kde.h>
#include "testutil.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoPattern.h>
#include "kis_transparency_mask.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include "layerstyles/kis_layer_style_projection_plane.h"
#include "kis_psd_layer_style.h"
void KisLayerStyleProjectionPlaneTest::test(KisPSDLayerStyleSP style, const QString testName)
{
const QRect imageRect(0, 0, 200, 200);
const QRect rFillRect(10, 10, 100, 100);
const QRect tMaskRect(50, 50, 20, 20);
const QRect partialSelectionRect(90, 50, 20, 20);
const QRect updateRect1(10, 10, 50, 100);
const QRect updateRect2(60, 10, 50, 100);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "styles test");
KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->addNode(layer);
KisLayerStyleProjectionPlane plane(layer.data(), style);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "00L_initial", testName);
//layer->paintDevice()->fill(rFillRect, KoColor(Qt::red, cs));
{
KisPainter gc(layer->paintDevice());
gc.setPaintColor(KoColor(Qt::red, cs));
gc.setFillStyle(KisPainter::FillStyleForegroundColor);
gc.paintEllipse(rFillRect);
}
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "01L_fill", testName);
KisPaintDeviceSP projection = new KisPaintDevice(cs);
{
const QRect changeRect = plane.changeRect(rFillRect, KisLayer::N_FILTHY);
- qDebug() << ppVar(rFillRect) << ppVar(changeRect);
+ dbgKrita << ppVar(rFillRect) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "02L_recalculate_fill", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "03P_apply_on_fill", testName);
}
//return;
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(tMaskRect, OPACITY_OPAQUE_U8);
transparencyMask->setSelection(selection);
image->addNode(transparencyMask, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "04L_mask_added", testName);
plane.recalculate(imageRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "05L_mask_added_recalculated", testName);
{
projection->clear();
KisPainter painter(projection);
plane.apply(&painter, imageRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "06P_apply_on_mask", testName);
}
selection->pixelSelection()->select(partialSelectionRect, OPACITY_OPAQUE_U8);
{
const QRect changeRect = plane.changeRect(partialSelectionRect, KisLayer::N_FILTHY);
projection->clear(changeRect);
- qDebug() << ppVar(partialSelectionRect) << ppVar(changeRect);
+ dbgKrita << ppVar(partialSelectionRect) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "07L_recalculate_partial", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "08P_apply_partial", testName);
}
// half updates
transparencyMask->setVisible(false);
{
const QRect changeRect = plane.changeRect(updateRect1, KisLayer::N_FILTHY);
projection->clear(changeRect);
- qDebug() << ppVar(updateRect1) << ppVar(changeRect);
+ dbgKrita << ppVar(updateRect1) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half1", testName);
}
{
const QRect changeRect = plane.changeRect(updateRect2, KisLayer::N_FILTHY);
projection->clear(changeRect);
- qDebug() << ppVar(updateRect2) << ppVar(changeRect);
+ dbgKrita << ppVar(updateRect2) << ppVar(changeRect);
plane.recalculate(changeRect, layer);
KIS_DUMP_DEVICE_2(layer->projection(), imageRect, "09L_recalculate_half1", testName);
KisPainter painter(projection);
plane.apply(&painter, changeRect);
KIS_DUMP_DEVICE_2(projection, imageRect, "10P_apply_half2", testName);
}
}
void KisLayerStyleProjectionPlaneTest::testShadow()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->dropShadow()->setSize(15);
style->dropShadow()->setDistance(15);
style->dropShadow()->setOpacity(70);
style->dropShadow()->setNoise(30);
style->dropShadow()->setEffectEnabled(true);
style->innerShadow()->setSize(10);
style->innerShadow()->setSpread(10);
style->innerShadow()->setDistance(5);
style->innerShadow()->setOpacity(70);
style->innerShadow()->setNoise(30);
style->innerShadow()->setEffectEnabled(true);
test(style, "shadow");
}
void KisLayerStyleProjectionPlaneTest::testGlow()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(30);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
test(style, "glow_outer");
}
#include "KoStopGradient.h"
void KisLayerStyleProjectionPlaneTest::testGlowGradient()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(10);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->outerGlow()->setGradient(gradient);
style->outerGlow()->setFillType(psd_fill_gradient);
test(style, "glow_outer_grad");
}
void KisLayerStyleProjectionPlaneTest::testGlowGradientJitter()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setSize(15);
style->outerGlow()->setSpread(10);
style->outerGlow()->setOpacity(70);
style->outerGlow()->setNoise(0);
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setColor(Qt::green);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->outerGlow()->setGradient(gradient);
style->outerGlow()->setFillType(psd_fill_gradient);
style->outerGlow()->setJitter(20);
test(style, "glow_outer_grad_jit");
}
void KisLayerStyleProjectionPlaneTest::testGlowInnerGradient()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->innerGlow()->setSize(15);
style->innerGlow()->setSpread(10);
style->innerGlow()->setOpacity(80);
style->innerGlow()->setNoise(10);
style->innerGlow()->setEffectEnabled(true);
style->innerGlow()->setColor(Qt::white);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->innerGlow()->setGradient(gradient);
style->innerGlow()->setFillType(psd_fill_gradient);
test(style, "glow_inner_grad");
style->innerGlow()->setFillType(psd_fill_solid_color);
style->innerGlow()->setSource(psd_glow_center);
test(style, "glow_inner_grad_center");
}
#include <KoCompositeOpRegistry.h>
void KisLayerStyleProjectionPlaneTest::testSatin()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->satin()->setSize(15);
style->satin()->setOpacity(80);
style->satin()->setAngle(180);
style->satin()->setEffectEnabled(true);
style->satin()->setColor(Qt::white);
style->satin()->setBlendMode(COMPOSITE_LINEAR_DODGE);
test(style, "satin");
}
void KisLayerStyleProjectionPlaneTest::testColorOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->colorOverlay()->setOpacity(80);
style->colorOverlay()->setEffectEnabled(true);
style->colorOverlay()->setColor(Qt::white);
style->colorOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
test(style, "color_overlay");
}
void KisLayerStyleProjectionPlaneTest::testGradientOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->gradientOverlay()->setAngle(90);
style->gradientOverlay()->setOpacity(80);
style->gradientOverlay()->setEffectEnabled(true);
style->gradientOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
style->gradientOverlay()->setAlignWithLayer(true);
style->gradientOverlay()->setScale(100);
style->gradientOverlay()->setStyle(psd_gradient_style_diamond);
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->gradientOverlay()->setGradient(gradient);
test(style, "grad_overlay");
}
void KisLayerStyleProjectionPlaneTest::testPatternOverlay()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->patternOverlay()->setOpacity(80);
style->patternOverlay()->setEffectEnabled(true);
style->patternOverlay()->setBlendMode(COMPOSITE_LINEAR_DODGE);
style->patternOverlay()->setScale(100);
style->patternOverlay()->setAlignWithLayer(false);
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
KoPattern pattern(fileName);
QVERIFY(pattern.load());
style->patternOverlay()->setPattern(&pattern);
test(style, "pat_overlay");
}
void KisLayerStyleProjectionPlaneTest::testStroke()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->stroke()->setColor(Qt::blue);
style->stroke()->setOpacity(80);
style->stroke()->setEffectEnabled(true);
style->stroke()->setBlendMode(COMPOSITE_OVER);
style->stroke()->setSize(3);
style->stroke()->setPosition(psd_stroke_center);
test(style, "stroke_col_ctr");
style->stroke()->setPosition(psd_stroke_outside);
test(style, "stroke_col_out");
style->stroke()->setPosition(psd_stroke_inside);
test(style, "stroke_col_in");
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
KoPattern pattern(fileName);
QVERIFY(pattern.load());
style->stroke()->setPattern(&pattern);
style->stroke()->setFillType(psd_fill_pattern);
test(style, "stroke_pat");
QLinearGradient testGradient;
testGradient.setColorAt(0.0, Qt::white);
testGradient.setColorAt(0.5, Qt::green);
testGradient.setColorAt(1.0, Qt::black);
testGradient.setSpread(QGradient::ReflectSpread);
QSharedPointer<KoStopGradient> gradient(
KoStopGradient::fromQGradient(&testGradient));
style->stroke()->setGradient(gradient);
style->stroke()->setFillType(psd_fill_gradient);
test(style, "stroke_grad");
}
#include "layerstyles/gimp_bump_map.h"
void KisLayerStyleProjectionPlaneTest::testBumpmap()
{
KisPixelSelectionSP device = new KisPixelSelection();
const int numCycles = 30;
const int step = 5;
QRect applyRect(200, 100, 100, 100);
QRect fillRect(210, 110, 80, 80);
quint8 selectedness = 256 - numCycles * step;
for (int i = 0; i < numCycles; i++) {
device->select(fillRect, selectedness);
fillRect = kisGrowRect(fillRect, -1);
selectedness += step;
}
KIS_DUMP_DEVICE_2(device, applyRect, "00_initial", "bumpmap");
bumpmap_vals_t bmvals;
bmvals.azimuth = 240;
bmvals.elevation = 30;
bmvals.depth = 50;
bmvals.ambient = 128;
bmvals.compensate = false;
bmvals.invert = false;
bmvals.type = 0;
bumpmap(device, applyRect, bmvals);
KIS_DUMP_DEVICE_2(device, applyRect, "01_bumpmapped", "bumpmap");
}
void KisLayerStyleProjectionPlaneTest::testBevel()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->bevelAndEmboss()->setEffectEnabled(true);
style->bevelAndEmboss()->setAngle(135);
style->bevelAndEmboss()->setAltitude(45);
style->bevelAndEmboss()->setDepth(100);
style->bevelAndEmboss()->setHighlightColor(Qt::white);
style->bevelAndEmboss()->setHighlightBlendMode(COMPOSITE_OVER);
style->bevelAndEmboss()->setHighlightOpacity(100);
style->bevelAndEmboss()->setShadowColor(Qt::black);
style->bevelAndEmboss()->setShadowBlendMode(COMPOSITE_OVER);
style->bevelAndEmboss()->setShadowOpacity(100);
QString fileName(TestUtil::fetchDataFileLazy("pattern.pat"));
KoPattern pattern(fileName);
QVERIFY(pattern.load());
style->bevelAndEmboss()->setTexturePattern(&pattern);
style->bevelAndEmboss()->setTextureEnabled(true);
style->bevelAndEmboss()->setTextureDepth(-10);
style->bevelAndEmboss()->setTextureInvert(false);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_up");
style->bevelAndEmboss()->setTextureInvert(true);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_up_invert_texture");
style->bevelAndEmboss()->setTextureInvert(false);
style->bevelAndEmboss()->setStyle(psd_bevel_outer_bevel);
style->bevelAndEmboss()->setDirection(psd_direction_down);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_outer_down");
style->bevelAndEmboss()->setStyle(psd_bevel_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_emboss_up");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_pillow_up");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_down);
style->bevelAndEmboss()->setSoften(0);
test(style, "bevel_pillow_down");
style->bevelAndEmboss()->setStyle(psd_bevel_pillow_emboss);
style->bevelAndEmboss()->setDirection(psd_direction_up);
style->bevelAndEmboss()->setSoften(3);
test(style, "bevel_pillow_up_soft");
}
QTEST_KDEMAIN(KisLayerStyleProjectionPlaneTest, GUI)
diff --git a/krita/image/tests/kis_liquify_transform_worker_test.cpp b/krita/image/tests/kis_liquify_transform_worker_test.cpp
index 6a57c6b4c5b..0dc188b1a3f 100644
--- a/krita/image/tests/kis_liquify_transform_worker_test.cpp
+++ b/krita/image/tests/kis_liquify_transform_worker_test.cpp
@@ -1,157 +1,157 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_liquify_transform_worker_test.h"
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoProgressUpdater.h>
#include <KoUpdater.h>
#include "testutil.h"
#include <kis_liquify_transform_worker.h>
#include <kis_algebra_2d.h>
void KisLiquifyTransformWorkerTest::testPoints()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
const int pixelPrecision = 8;
KisLiquifyTransformWorker worker(dev->exactBounds(),
updater,
pixelPrecision);
QBENCHMARK_ONCE {
worker.translatePoints(QPointF(100,100),
QPointF(50, 0),
50, false, 0.2);
worker.scalePoints(QPointF(400,100),
0.9,
50, false, 0.2);
worker.undoPoints(QPointF(400,100),
1.0,
50);
worker.scalePoints(QPointF(400,300),
0.5,
50, false, 0.2);
worker.scalePoints(QPointF(100,300),
-0.5,
30, false, 0.2);
worker.rotatePoints(QPointF(100,500),
M_PI / 4,
50, false, 0.2);
}
worker.run(dev);
QImage result = dev->convertToQImage(0);
TestUtil::checkQImage(result, "liquify_transform_test", "liquify_dev", "unity");
}
void KisLiquifyTransformWorkerTest::testPointsQImage()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
const int pixelPrecision = 8;
KisLiquifyTransformWorker worker(dev->exactBounds(),
updater,
pixelPrecision);
worker.translatePoints(QPointF(100,100),
QPointF(50, 0),
50, false, 0.2);
QRect rc = dev->exactBounds();
dev->setX(50);
dev->setY(50);
worker.run(dev);
rc |= dev->exactBounds();
QImage resultDev = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
TestUtil::checkQImage(resultDev, "liquify_transform_test", "liquify_qimage", "refDevice");
QTransform imageToThumbTransform =
QTransform::fromScale(0.5, 0.5);
QImage srcImage(image);
image = QImage(image.size(), QImage::Format_ARGB32);
QPainter gc(&image);
gc.setTransform(imageToThumbTransform);
gc.drawImage(QPoint(), srcImage);
QPointF newOffset;
QImage result = worker.runOnQImage(image, QPointF(10, 10), imageToThumbTransform, &newOffset);
- qDebug() << ppVar(newOffset);
+ dbgKrita << ppVar(newOffset);
TestUtil::checkQImage(result, "liquify_transform_test", "liquify_qimage", "resultImage");
}
void KisLiquifyTransformWorkerTest::testIdentityTransform()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QRect rc(0,0,13,23);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(rc, KoColor(Qt::blue, cs));
const int pixelPrecision = 8;
KisLiquifyTransformWorker worker(dev->exactBounds(),
updater,
pixelPrecision);
worker.run(dev);
QImage result = dev->convertToQImage(0, rc);
TestUtil::checkQImage(result, "liquify_transform_test", "liquify_dev", "identity");
}
QTEST_KDEMAIN(KisLiquifyTransformWorkerTest, GUI)
diff --git a/krita/image/tests/kis_node_test.cpp b/krita/image/tests/kis_node_test.cpp
index f6b3ad169a7..ae8022770e4 100644
--- a/krita/image/tests/kis_node_test.cpp
+++ b/krita/image/tests/kis_node_test.cpp
@@ -1,458 +1,458 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_node_test.h"
#include <qtest_kde.h>
#include <limits.h>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_node.h"
#include "kis_node_graph_listener.h"
#include <KoProperties.h>
#include "testutil.h"
void KisNodeTest::testCreation()
{
TestUtil::TestGraphListener graphListener;
KisNode * node = new TestNodeA();
QVERIFY(node->graphListener() == 0);
node->setGraphListener(&graphListener);
QVERIFY(node->graphListener() != 0);
// Test contract for initial state
QVERIFY(node->parent() == 0);
QVERIFY(node->firstChild() == 0);
QVERIFY(node->lastChild() == 0);
QVERIFY(node->prevSibling() == 0);
QVERIFY(node->nextSibling() == 0);
QVERIFY(node->childCount() == 0);
QVERIFY(node->at(0) == 0);
QVERIFY(node->at(UINT_MAX) == 0);
QVERIFY(node->index(0) == -1);
delete node;
}
void KisNodeTest::testOrdering()
{
TestUtil::TestGraphListener graphListener;
KisNodeSP root = new TestNodeA();
root->setGraphListener(&graphListener);
KisNodeSP node1 = new TestNodeA();
KisNodeSP node2 = new TestNodeA();
KisNodeSP node3 = new TestNodeA();
KisNodeSP node4 = new TestNodeA();
/*
+---------+
| node 4 |
| node 2 |
| node 3 |
| node 1 |
|root |
+---------+
*/
graphListener.resetBools();
QVERIFY(root->lastChild() == 0);
root->add(node1, root->lastChild());
QVERIFY(graphListener.beforeInsertRow == true);
QVERIFY(graphListener.afterInsertRow == true);
QVERIFY(graphListener.beforeRemoveRow == false);
QVERIFY(graphListener.afterRemoveRow == false);
QVERIFY(root->firstChild() == node1);
QVERIFY(root->lastChild() == node1);
graphListener.resetBools();
QVERIFY(root->lastChild() == node1);
root->add(node2, root->lastChild());
QVERIFY(graphListener.beforeInsertRow == true);
QVERIFY(graphListener.afterInsertRow == true);
QVERIFY(graphListener.beforeRemoveRow == false);
QVERIFY(graphListener.afterRemoveRow == false);
QVERIFY(root->firstChild() == node1);
QVERIFY(root->lastChild() == node2);
graphListener.resetBools();
QVERIFY(root->lastChild() == node2);
root->add(node3, node1);
QVERIFY(root->lastChild() == node2);
QVERIFY(graphListener.beforeInsertRow == true);
QVERIFY(graphListener.afterInsertRow == true);
QVERIFY(graphListener.beforeRemoveRow == false);
QVERIFY(graphListener.afterRemoveRow == false);
QVERIFY(root->firstChild() == node1);
QVERIFY(root->lastChild() == node2);
graphListener.resetBools();
root->add(node4, root->lastChild());
QVERIFY(graphListener.beforeInsertRow == true);
QVERIFY(graphListener.afterInsertRow == true);
QVERIFY(graphListener.beforeRemoveRow == false);
QVERIFY(graphListener.afterRemoveRow == false);
QVERIFY(root->firstChild() == node1);
QVERIFY(root->lastChild() == node4);
graphListener.resetBools();
QVERIFY(root->childCount() == 4);
QVERIFY(node1->parent() == root);
QVERIFY(node2->parent() == root);
QVERIFY(node3->parent() == root);
QVERIFY(node4->parent() == root);
QVERIFY(root->firstChild() == node1);
QVERIFY(root->lastChild() == node4);
QVERIFY(root->at(0) == node1);
QVERIFY(root->at(1) == node3);
QVERIFY(root->at(2) == node2);
QVERIFY(root->at(3) == node4);
QVERIFY(root->index(node1) == 0);
QVERIFY(root->index(node3) == 1);
QVERIFY(root->index(node2) == 2);
QVERIFY(root->index(node4) == 3);
QVERIFY(node4->prevSibling() == node2);
QVERIFY(node3->prevSibling() == node1);
QVERIFY(node2->prevSibling() == node3);
QVERIFY(node1->prevSibling() == 0);
QVERIFY(node4->nextSibling() == 0);
QVERIFY(node3->nextSibling() == node2);
QVERIFY(node2->nextSibling() == node4);
QVERIFY(node1->nextSibling() == node3);
/*
+---------+
| node 3 |
| node 4 |
| node 2 |
| node 1 |
|root |
+---------+
*/
graphListener.resetBools();
QVERIFY(root->remove(root->at(3)) == true);
QVERIFY(node4->parent() == 0);
QVERIFY(graphListener.beforeInsertRow == false);
QVERIFY(graphListener.afterInsertRow == false);
QVERIFY(graphListener.beforeRemoveRow == true);
QVERIFY(graphListener.afterRemoveRow == true);
QVERIFY(root->childCount() == 3);
QVERIFY(root->lastChild() == node2);
QVERIFY(root->firstChild() == node1);
QVERIFY(node4->prevSibling() == 0);
QVERIFY(node4->nextSibling() == 0);
graphListener.resetBools();
node3->add(node4, node3->lastChild());
QVERIFY(graphListener.beforeInsertRow == true);
QVERIFY(graphListener.afterInsertRow == true);
QVERIFY(graphListener.beforeRemoveRow == false);
QVERIFY(graphListener.afterRemoveRow == false);
QVERIFY(root->childCount() == 3);
QVERIFY(root->lastChild() == node2);
QVERIFY(root->firstChild() == node1);
QVERIFY(node3->childCount() == 1);
QVERIFY(node3->firstChild() == node4);
QVERIFY(node3->lastChild() == node4);
QVERIFY(node4->prevSibling() == 0);
QVERIFY(node4->nextSibling() == 0);
QVERIFY(root->remove(node4) == false);
graphListener.resetBools();
node3->remove(node4);
QVERIFY(graphListener.beforeInsertRow == false);
QVERIFY(graphListener.afterInsertRow == false);
QVERIFY(graphListener.beforeRemoveRow == true);
QVERIFY(graphListener.afterRemoveRow == true);
QVERIFY(node3->childCount() == 0);
QVERIFY(node4->parent() == 0);
QVERIFY(root->childCount() == 3);
QVERIFY(root->lastChild() == node2);
QVERIFY(root->firstChild() == node1);
QVERIFY(node4->prevSibling() == 0);
QVERIFY(node4->nextSibling() == 0);
}
void KisNodeTest::testSetDirty()
{
// Create a node graph with two branches
/*
node2
node4
node6
node5
node5
node3
node1
root
*/
KisNodeSP root = new TestNode();
root->setName("root");
KisNodeSP node1 = new TestNode();
node1->setName("node1");
QVERIFY(root->add(node1, 0));
KisNodeSP node2 = new TestNode();
node2->setName("node2");
QVERIFY(root->add(node2, node1));
KisNodeSP node3 = new TestNode();
node3->setName("node3");
QVERIFY(node1->add(node3, 0));
KisNodeSP node4 = new TestNode();
node4->setName("node4");
QVERIFY(node1->add(node4, node3));
KisNodeSP node5 = new TestNode();
node5->setName("node5");
QVERIFY(node3->add(node5, 0));
KisNodeSP node6 = new TestNode();
node6->setName("node6");
QVERIFY(node5->add(node6, 0));
KisNodeSP node7 = new TestNode();
node7->setName("node7");
QVERIFY(node6->add(node7, 0));
#if 0 // XXX: rewrite tests after redesign to update strategies
node1->setDirty();
QVERIFY(node1->isDirty());
QVERIFY(!node2->isDirty());
QVERIFY(root->isDirty());
root->setClean();
QVERIFY(!root->isDirty());
node1->setClean();
QVERIFY(!node1->isDirty());
node7->setDirty(QRect(10, 10, 100, 100));
QVERIFY(node7->isDirty());
QVERIFY(node7->isDirty(QRect(5, 5, 15, 15)));
QVERIFY(root->isDirty(QRect(5, 5, 15, 15)));
QVERIFY(!root->isDirty(QRect(-10, -10, 20, 20)));
QVERIFY(!node2->isDirty());
node7->setClean(QRect(10, 10, 10, 10));
QVERIFY(!node7->isDirty(QRect(10, 10, 10, 10)));
QVERIFY(node7->isDirty());
QVERIFY(node7->isDirty(QRect(0, 0, 50, 50)));
#endif
}
void KisNodeTest::testChildNodes()
{
KisNodeSP root = new TestNodeA();
KisNodeSP a = new TestNodeA();
root->add(a, 0);
a->setVisible(true);
a->setUserLocked(true);
KisNodeSP b = new TestNodeB();
root->add(b, 0);
b->setVisible(false);
b->setUserLocked(true);
KisNodeSP c = new TestNodeC();
root->add(c, 0);
c->setVisible(false);
c->setVisible(false);
QList<KisNodeSP> allNodes = root->childNodes(QStringList(), KoProperties());
QCOMPARE((int) allNodes.count(), 3); // a, b, c
QStringList nodeTypes;
nodeTypes << "TestNodeA" << "TestNodeB";
QList<KisNodeSP> subSetOfNodeTypes = root->childNodes(nodeTypes, KoProperties());
QVERIFY(subSetOfNodeTypes.count() == 2); // a, b
nodeTypes.clear();
nodeTypes << "TestNodeB" << "TestNodeC";
KoProperties props;
props.setProperty("visibile", false);
props.setProperty("locked", true);
QList<KisNodeSP> subsetOfTypesAndProps = root->childNodes(nodeTypes, props);
QVERIFY(subsetOfTypesAndProps.count() == 1); // b
KoProperties props2;
props.setProperty("visibile", false);
QList<KisNodeSP> subSetOfProps = root->childNodes(QStringList(), props);
QVERIFY(subSetOfProps.count() == 2); // b, c
}
void KisNodeTest::testDirtyRegion()
{
#if 0 // Rewrite
KisNodeSP root = new TestNodeA();
root->setDirty(QRect(0, 0, 100, 100));
root->setDirty(QRect(50, 50, 100, 100));
QRegion dirtyRegion = root->dirtyRegion(QRect(0, 0, 200, 200));
QVector<QRect> rects = dirtyRegion.rects();
QVERIFY(rects.count() == 3);
QVERIFY(rects[0] == QRect(0, 0, 100, 50));
QVERIFY(rects[1] == QRect(0, 50, 150, 50));
QVERIFY(rects[2] == QRect(50, 100, 100, 50));
#endif
}
#define NUM_CYCLES 100000
#define NUM_THREADS 30
class KisNodeTest::VisibilityKiller : public QRunnable {
public:
VisibilityKiller(KisNodeSP victimNode, KisNodeSP nastyChild, bool /*isWriter*/)
: m_victimNode(victimNode),
m_nastyChild(nastyChild)
{}
void run() {
int visibility = 0;
for(int i = 0; i < NUM_CYCLES; i++) {
if(i % 3 == 0) {
m_nastyChild->setVisible(visibility++ & 0x1);
- // qDebug() << "visibility" << i << m_nastyChild->visible();
+ // dbgKrita << "visibility" << i << m_nastyChild->visible();
}
else if (i%3 == 1){
KoProperties props;
props.setProperty("visible", true);
QList<KisNodeSP> visibleNodes =
m_victimNode->childNodes(QStringList("TestNodeB"), props);
foreach(KisNodeSP node, visibleNodes) {
m_nastyChild->setVisible(visibility++ & 0x1);
}
- // qDebug() << visibleNodes;
+ // dbgKrita << visibleNodes;
}
else {
Q_ASSERT(m_victimNode->firstChild());
Q_ASSERT(m_victimNode->lastChild());
m_victimNode->firstChild()->setVisible(visibility++ & 0x1);
m_victimNode->lastChild()->setVisible(visibility++ & 0x1);
}
}
}
private:
KisNodeSP m_victimNode;
KisNodeSP m_nastyChild;
};
template <class KillerClass>
void KisNodeTest::propertiesStressTestImpl() {
KisNodeSP root = new TestNodeA();
KisNodeSP a = new TestNodeA();
KisNodeSP b = new TestNodeB();
KisNodeSP c = new TestNodeC();
root->add(a, 0);
root->add(b, 0);
root->add(c, 0);
a->setVisible(true);
b->setVisible(true);
c->setVisible(true);
a->setUserLocked(true);
b->setUserLocked(true);
c->setUserLocked(true);
QThreadPool threadPool;
threadPool.setMaxThreadCount(NUM_THREADS);
for(int i = 0; i< NUM_THREADS; i++) {
KillerClass *killer = new KillerClass(root, b, i == 0);
threadPool.start(killer);
}
threadPool.waitForDone();
}
void KisNodeTest::propertiesStressTest() {
propertiesStressTestImpl<VisibilityKiller>();
}
class KisNodeTest::GraphKiller : public QRunnable {
public:
GraphKiller(KisNodeSP parentNode, KisNodeSP childNode, bool isWriter)
: m_parentNode(parentNode),
m_childNode(childNode),
m_isWriter(isWriter)
{}
void run() {
int numCycles = qMax(10000, NUM_CYCLES / 100);
for(int i = 0; i < numCycles; i++) {
if (m_isWriter) {
m_parentNode->remove(m_childNode);
m_parentNode->add(m_childNode, 0);
} else {
KisNodeSP a = m_parentNode->firstChild();
KisNodeSP b = m_parentNode->lastChild();
if (a) {
a->parent();
a->nextSibling();
a->prevSibling();
}
if (b) {
b->parent();
b->nextSibling();
b->prevSibling();
}
m_parentNode->at(0);
m_parentNode->index(m_childNode);
}
if (i % 1000 == 0) {
- //qDebug() << "Alive";
+ //dbgKrita << "Alive";
}
}
}
private:
KisNodeSP m_parentNode;
KisNodeSP m_childNode;
bool m_isWriter;
};
void KisNodeTest::graphStressTest() {
propertiesStressTestImpl<GraphKiller>();
}
QTEST_KDEMAIN(KisNodeTest, NoGUI)
diff --git a/krita/image/tests/kis_paint_device_test.cpp b/krita/image/tests/kis_paint_device_test.cpp
index 4188be6f7f7..846b3cab320 100644
--- a/krita/image/tests/kis_paint_device_test.cpp
+++ b/krita/image/tests/kis_paint_device_test.cpp
@@ -1,1378 +1,1378 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paint_device_test.h"
#include <qtest_kde.h>
#include <QTime>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoStore.h>
#include "kis_paint_device_writer.h"
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_selection.h"
#include "kis_datamanager.h"
#include "kis_global.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_image.h"
class KisFakePaintDeviceWriter : public KisPaintDeviceWriter {
public:
KisFakePaintDeviceWriter(KoStore *store)
: m_store(store)
{
}
bool write(const QByteArray &data) {
return (m_store->write(data) == data.size());
}
bool write(const char* data, qint64 length) {
return (m_store->write(data, length) == length);
}
KoStore *m_store;
};
void KisPaintDeviceTest::testCreation()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->objectName().isEmpty());
dev = new KisPaintDevice(cs);
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->x() == 0);
QVERIFY(dev->y() == 0);
QVERIFY(dev->pixelSize() == cs->pixelSize());
QVERIFY(dev->channelCount() == cs->channelCount());
QVERIFY(dev->dataManager() != 0);
KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
dev = new KisPaintDevice(layer.data(), cs);
QVERIFY(*dev->colorSpace() == *cs);
QVERIFY(dev->x() == 0);
QVERIFY(dev->y() == 0);
QVERIFY(dev->pixelSize() == cs->pixelSize());
QVERIFY(dev->channelCount() == cs->channelCount());
QVERIFY(dev->dataManager() != 0);
// Let the layer go out of scope and see what happens
{
KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250);
dev = new KisPaintDevice(l2.data(), cs);
}
}
void KisPaintDeviceTest::testStore()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KoStore * readStore =
KoStore::createStore(QString(FILES_DATA_DIR) + QDir::separator() + "store_test.kra", KoStore::Read);
readStore->open("built image/layers/layer0");
QVERIFY(dev->read(readStore->device()));
readStore->close();
delete readStore;
QVERIFY(dev->exactBounds() == QRect(0, 0, 100, 100));
KoStore * writeStore =
KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Write);
KisFakePaintDeviceWriter fakeWriter(writeStore);
writeStore->open("built image/layers/layer0");
QVERIFY(dev->write(fakeWriter));
writeStore->close();
delete writeStore;
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
readStore =
KoStore::createStore(QString(FILES_OUTPUT_DIR) + QDir::separator() + "store_test_out.kra", KoStore::Read);
readStore->open("built image/layers/layer0");
QVERIFY(dev2->read(readStore->device()));
readStore->close();
delete readStore;
QVERIFY(dev2->exactBounds() == QRect(0, 0, 100, 100));
QPoint pt;
if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
}
}
void KisPaintDeviceTest::testGeometry()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, pixel);
dev->fill(0, 0, 512, 512, pixel);
QCOMPARE(dev->exactBounds(), QRect(0, 0, 512, 512));
QCOMPARE(dev->extent(), QRect(0, 0, 512, 512));
dev->move(10, 10);
QCOMPARE(dev->exactBounds(), QRect(10, 10, 512, 512));
QCOMPARE(dev->extent(), QRect(10, 10, 512, 512));
dev->crop(50, 50, 50, 50);
QCOMPARE(dev->exactBounds(), QRect(50, 50, 50, 50));
QCOMPARE(dev->extent(), QRect(10, 10, 128, 128));
QColor c;
dev->clear(QRect(50, 50, 50, 50));
dev->pixel(80, 80, &c);
QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
dev->fill(0, 0, 512, 512, pixel);
dev->pixel(80, 80, &c);
QVERIFY(c == Qt::white);
QVERIFY(c.alpha() == OPACITY_OPAQUE_U8);
dev->clear();
dev->pixel(80, 80, &c);
QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
QVERIFY(dev->extent().isEmpty());
QVERIFY(dev->exactBounds().isEmpty());
}
void KisPaintDeviceTest::testClear()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
dev->clear();
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
QRect fillRect1(50, 100, 150, 100);
dev->fill(fillRect1, KoColor(Qt::red, cs));
QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
QCOMPARE(dev->exactBounds(), fillRect1);
dev->clear(QRect(100, 100, 100, 100));
QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
QCOMPARE(dev->exactBounds(), QRect(50, 100, 50, 100));
dev->clear();
QVERIFY(!dev->extent().isValid());
QVERIFY(!dev->exactBounds().isValid());
}
void KisPaintDeviceTest::testCrop()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, pixel);
dev->fill(-14, 8, 433, 512, pixel);
QVERIFY(dev->exactBounds() == QRect(-14, 8, 433, 512));
// Crop inside
dev->crop(50, 50, 150, 150);
QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
// Crop outside, pd should not grow
dev->crop(0, 0, 1000, 1000);
QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
}
void KisPaintDeviceTest::testRoundtripReadWrite()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
dev->convertFromQImage(image, 0);
quint8* bytes = new quint8[cs->pixelSize() * image.width() * image.height()];
memset(bytes, 0, image.width() * image.height() * dev->pixelSize());
dev->readBytes(bytes, image.rect());
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->writeBytes(bytes, image.rect());
QVERIFY(dev2->exactBounds() == image.rect());
dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png");
QPoint pt;
if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
}
}
void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
{
QString profile1("no profile");
QString profile2("no profile");
if (srcCs->profile())
profile1 = srcCs->profile()->name();
if (dstCs->profile())
profile2 = dstCs->profile()->name();
QWARN(QString("Failed %1 %2 -> %3 %4 %5")
.arg(srcCs->name())
.arg(profile1)
.arg(dstCs->name())
.arg(profile2)
.arg(reason)
.toLatin1());
}
void KisPaintDeviceTest::testColorSpaceConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
dev->convertFromQImage(image, 0);
dev->move(10, 10); // Unalign with tile boundaries
KUndo2Command* cmd = dev->convertTo(dstCs);
QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
QCOMPARE(dev->pixelSize(), dstCs->pixelSize());
QVERIFY(*dev->colorSpace() == *dstCs);
delete cmd;
}
void KisPaintDeviceTest::testRoundtripConversion()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("kis_paint_device_test_test_roundtrip_qimage.png");
result.save("kis_paint_device_test_test_roundtrip_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testFastBitBlt()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dstDev = new KisPaintDevice(cs);
KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
srcDev->convertFromQImage(image, 0);
QRect cloneRect(100,100,200,200);
QPoint errpoint;
QVERIFY(dstDev->fastBitBltPossible(srcDev));
dstDev->fastBitBlt(srcDev, cloneRect);
QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
// Test Rough version
dstDev->clear();
dstDev->fastBitBltRough(srcDev, cloneRect);
srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
srcDev->move(10,10);
QVERIFY(!dstDev->fastBitBltPossible(srcDev));
}
void KisPaintDeviceTest::testMakeClone()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
srcDev->convertFromQImage(image, 0);
srcDev->move(10,10);
const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16();
KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS);
dstDev->move(1000,1000);
QVERIFY(!dstDev->fastBitBltPossible(srcDev));
QRect cloneRect(100,100,200,200);
QPoint errpoint;
dstDev->makeCloneFrom(srcDev, cloneRect);
QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace());
QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize());
QCOMPARE(dstDev->x(), srcDev->x());
QCOMPARE(dstDev->y(), srcDev->y());
QCOMPARE(dstDev->exactBounds(), cloneRect);
QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
cloneRect.width(), cloneRect.height());
if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) {
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testThumbnail()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
{
KisPaintDeviceSP thumb = dev->createThumbnailDevice(50, 50);
QRect rc = thumb->exactBounds();
QVERIFY(rc.width() <= 50);
QVERIFY(rc.height() <= 50);
}
{
QImage thumb = dev->createThumbnail(50, 50);
QVERIFY(!thumb.isNull());
QVERIFY(thumb.width() <= 50);
QVERIFY(thumb.height() <= 50);
image.save("kis_paint_device_test_test_thumbnail.png");
}
}
void KisPaintDeviceTest::testThumbnailDeviceWithOffset()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
dev->setX(10);
dev->setY(10);
QImage thumb = dev->createThumbnail(640,441,QRect(10,10,640,441));
image.save("oring.png");
thumb.save("thumb.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, thumb, image));
}
void KisPaintDeviceTest::testCaching()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
quint8* blackPixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::black, blackPixel);
dev->fill(0, 0, 512, 512, whitePixel);
QImage thumb1 = dev->createThumbnail(50, 50);
QRect exactBounds1 = dev->exactBounds();
dev->fill(0, 0, 768, 768, blackPixel);
QImage thumb2 = dev->createThumbnail(50, 50);
QRect exactBounds2 = dev->exactBounds();
dev->move(10, 10);
QImage thumb3 = dev->createThumbnail(50, 50);
QRect exactBounds3 = dev->exactBounds();
dev->crop(50, 50, 50, 50);
QImage thumb4 = dev->createThumbnail(50, 50);
QRect exactBounds4 = dev->exactBounds();
QVERIFY(thumb1 != thumb2);
QVERIFY(thumb2 == thumb3); // Cache miss, but image is the same
QVERIFY(thumb3 != thumb4);
QVERIFY(thumb4 != thumb1);
QCOMPARE(exactBounds1, QRect(0,0,512,512));
QCOMPARE(exactBounds2, QRect(0,0,768,768));
QCOMPARE(exactBounds3, QRect(10,10,768,768));
QCOMPARE(exactBounds4, QRect(50,50,50,50));
}
void KisPaintDeviceTest::testRegion()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* whitePixel = new quint8[cs->pixelSize()];
cs->fromQColor(Qt::white, whitePixel);
dev->fill(0, 0, 10, 10, whitePixel);
dev->fill(70, 70, 10, 10, whitePixel);
dev->fill(129, 0, 10, 10, whitePixel);
dev->fill(0, 1030, 10, 10, whitePixel);
QRegion referenceRegion;
referenceRegion += QRect(0,0,64,64);
referenceRegion += QRect(64,64,64,64);
referenceRegion += QRect(128,0,64,64);
referenceRegion += QRect(0,1024,64,64);
QCOMPARE(dev->exactBounds(), QRect(0,0,139,1040));
QCOMPARE(dev->region(), referenceRegion);
}
void KisPaintDeviceTest::testPixel()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QColor c = Qt::red;
quint8 opacity = 125;
c.setAlpha(opacity);
dev->setPixel(5, 5, c);
QColor c2;
dev->pixel(5, 5, &c2);
QVERIFY(c == c2);
QVERIFY(opacity == c2.alpha());
}
void KisPaintDeviceTest::testPlanarReadWrite()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
quint8* pixel = new quint8[cs->pixelSize()];
cs->fromQColor(QColor(255, 200, 155, 100), pixel);
dev->fill(0, 0, 5000, 5000, pixel);
delete[] pixel;
QColor c1;
dev->pixel(5, 5, &c1);
QVector<quint8*> planes = dev->readPlanarBytes(500, 500, 100, 100);
QVector<quint8*> swappedPlanes;
QCOMPARE((int)planes.size(), (int)dev->channelCount());
for (int i = 0; i < 100*100; i++) {
// BGRA encoded
QVERIFY(planes.at(2)[i] == 255);
QVERIFY(planes.at(1)[i] == 200);
QVERIFY(planes.at(0)[i] == 155);
QVERIFY(planes.at(3)[i] == 100);
}
for (uint i = 1; i < dev->channelCount() + 1; ++i) {
swappedPlanes.append(planes[dev->channelCount() - i]);
}
dev->writePlanarBytes(swappedPlanes, 0, 0, 100, 100);
dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar.png");
dev->pixel(5, 5, &c1);
QVERIFY(c1.red() == 200);
QVERIFY(c1.green() == 255);
QVERIFY(c1.blue() == 100);
QVERIFY(c1.alpha() == 155);
dev->pixel(75, 50, &c1);
QVERIFY(c1.red() == 200);
QVERIFY(c1.green() == 255);
QVERIFY(c1.blue() == 100);
QVERIFY(c1.alpha() == 155);
// check if one of the planes is Null.
Q_ASSERT(planes.size() == 4);
delete [] planes[2];
planes[2] = 0;
dev->writePlanarBytes(planes, 0, 0, 100, 100);
dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar_noR.png");
dev->pixel(75, 50, &c1);
QCOMPARE(c1.red(), 200);
QCOMPARE(c1.green(), 200);
QCOMPARE(c1.blue(), 155);
QCOMPARE(c1.alpha(), 100);
QVector<quint8*>::iterator i;
for (i = planes.begin(); i != planes.end(); ++i)
{
delete [] *i;
}
swappedPlanes.clear();
}
void KisPaintDeviceTest::testBltPerformance()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP fdev = new KisPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
QTime t;
t.start();
int x;
for (x = 0; x < 1000; ++x) {
KisPainter gc(dev);
gc.bitBlt(QPoint(0, 0), fdev, image.rect());
}
- qDebug() << x
+ dbgKrita << x
<< "blits"
<< " done in "
<< t.elapsed()
<< "ms";
}
void KisPaintDeviceTest::testDeviceDuplication()
{
QRect fillRect(0,0,64,64);
quint8 fillPixel[4]={255,255,255,255};
QRect clearRect(10,10,20,20);
QImage referenceImage;
QImage resultImage;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP device = new KisPaintDevice(cs);
-// qDebug()<<"FILLING";
+// dbgKrita<<"FILLING";
device->fill(fillRect.left(), fillRect.top(),
fillRect.width(), fillRect.height(),fillPixel);
referenceImage = device->convertToQImage(0);
KisTransaction transaction1(device);
-// qDebug()<<"CLEARING";
+// dbgKrita<<"CLEARING";
device->clear(clearRect);
transaction1.revert();
resultImage = device->convertToQImage(0);
QVERIFY(resultImage == referenceImage);
KisPaintDeviceSP clone = new KisPaintDevice(*device);
KisTransaction transaction(clone);
-// qDebug()<<"CLEARING";
+// dbgKrita<<"CLEARING";
clone->clear(clearRect);
transaction.revert();
resultImage = clone->convertToQImage(0);
QVERIFY(resultImage == referenceImage);
}
void KisPaintDeviceTest::testSharedDataManager()
{
QRect fillRect(0,0,100,100);
quint8 fillPixel[4]={255,255,255,255};
QRect clearRect(10,10,20,20);
QImage srcImage;
QImage dstImage;
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP srcDevice = new KisPaintDevice(cs);
srcDevice->setX(10);
srcDevice->setY(20);
srcDevice->fill(fillRect.left(), fillRect.top(),
fillRect.width(), fillRect.height(),fillPixel);
KisPaintDeviceSP dstDevice = new KisPaintDevice(srcDevice->dataManager(), srcDevice);
QVERIFY(srcDevice->extent() == dstDevice->extent());
QVERIFY(srcDevice->exactBounds() == dstDevice->exactBounds());
QVERIFY(srcDevice->defaultBounds() == dstDevice->defaultBounds());
QVERIFY(srcDevice->x() == dstDevice->x());
QVERIFY(srcDevice->y() == dstDevice->y());
srcImage = srcDevice->convertToQImage(0);
dstImage = dstDevice->convertToQImage(0);
QVERIFY(srcImage == dstImage);
srcDevice->clear(clearRect);
srcImage = srcDevice->convertToQImage(0);
dstImage = dstDevice->convertToQImage(0);
QVERIFY(srcImage == dstImage);
}
void KisPaintDeviceTest::testTranslate()
{
QRect fillRect(0,0,64,64);
quint8 fillPixel[4]={255,255,255,255};
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP device = new KisPaintDevice(cs);
device->fill(fillRect.left(), fillRect.top(),
fillRect.width(), fillRect.height(),fillPixel);
device->setX(-10);
device->setY(10);
QCOMPARE(device->exactBounds(), QRect(-10,10,64,64));
QCOMPARE(device->extent(), QRect(-10,10,64,64));
}
void KisPaintDeviceTest::testOpacity()
{
// blt a semi-transparent image on a white paint device
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png");
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP fdev = new KisPaintDevice(cs);
fdev->convertFromQImage(image, 0);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
KisPainter gc(dev);
gc.bitBlt(QPoint(0, 0), fdev, image.rect());
QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png");
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) {
checkResult.save("kis_paint_device_test_test_blt_fixed_opactiy_expected.png");
result.save("kis_paint_device_test_test_blt_fixed_opacity_result.png");
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisPaintDeviceTest::testExactBoundsWeirdNullAlphaCase()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
dev->fill(QRect(10,10,10,10), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
const quint8 weirdPixelData[4] = {0,10,0,0};
KoColor weirdColor(weirdPixelData, cs);
dev->setPixel(6,6,weirdColor);
// such weird pixels should not change our opinion about
// device's size
QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
}
void KisPaintDeviceTest::benchmarkExactBoundsNullDefaultPixel()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
QRect fillRect(60,60, 1930, 1930);
dev->fill(fillRect, KoColor(Qt::white, cs));
QRect measuredRect;
QBENCHMARK {
// invalidate the cache
dev->setDirty();
measuredRect = dev->exactBounds();
}
QCOMPARE(measuredRect, fillRect);
}
void KisPaintDeviceTest::testNonDefaultPixelArea()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QVERIFY(dev->exactBounds().isEmpty());
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
KoColor defPixel(Qt::red, cs);
dev->setDefaultPixel(defPixel.data());
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
QRect fillRect(10,11,18,14);
dev->fill(fillRect, KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QCOMPARE(dev->nonDefaultPixelArea(), fillRect);
// non-default pixel variant should also handle weird pixels
const quint8 weirdPixelData[4] = {0,10,0,0};
KoColor weirdColor(weirdPixelData, cs);
dev->setPixel(100,100,weirdColor);
// such weird pixels should not change our opinion about
// device's size
QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
QCOMPARE(dev->nonDefaultPixelArea(), fillRect | QRect(100,100,1,1));
}
void KisPaintDeviceTest::testExactBoundsNonTransparent()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
KisPaintDeviceSP dev = layer->paintDevice();
QVERIFY(dev);
QRect imageRect(0,0,1000,1000);
KoColor defPixel(Qt::red, cs);
dev->setDefaultPixel(defPixel.data());
QCOMPARE(dev->exactBounds(), imageRect);
QVERIFY(dev->nonDefaultPixelArea().isEmpty());
KoColor fillPixel(Qt::white, cs);
dev->fill(imageRect, KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), imageRect);
QCOMPARE(dev->nonDefaultPixelArea(), imageRect);
dev->fill(QRect(1000,0, 1, 1000), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1000));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1000));
dev->fill(QRect(0,1000, 1000, 1), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1001));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1001));
dev->fill(QRect(0,-1, 1000, 1), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(0,-1,1001,1002));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,-1,1001,1002));
dev->fill(QRect(-1,0, 1, 1000), KoColor(Qt::white, cs));
QCOMPARE(dev->exactBounds(), QRect(-1,-1,1002,1002));
QCOMPARE(dev->nonDefaultPixelArea(), QRect(-1,-1,1002,1002));
}
KisPaintDeviceSP createWrapAroundPaintDevice(const KoColorSpace *cs)
{
struct TestingDefaultBounds : public KisDefaultBoundsBase {
QRect bounds() const {
return QRect(0,0,20,20);
}
bool wrapAroundMode() const {
return true;
}
};
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisDefaultBoundsBaseSP bounds = new TestingDefaultBounds();
dev->setDefaultBounds(bounds);
return dev;
}
void checkReadWriteRoundTrip(KisPaintDeviceSP dev,
const QRect &rc)
{
KisPaintDeviceSP deviceCopy = new KisPaintDevice(*dev.data());
QRect readRect(10, 10, 20, 20);
int bufSize = rc.width() * rc.height() * dev->pixelSize();
QScopedArrayPointer<quint8> buf1(new quint8[bufSize]);
deviceCopy->readBytes(buf1.data(), rc);
deviceCopy->clear();
QVERIFY(deviceCopy->extent().isEmpty());
QScopedArrayPointer<quint8> buf2(new quint8[bufSize]);
deviceCopy->writeBytes(buf1.data(), rc);
deviceCopy->readBytes(buf2.data(), rc);
QVERIFY(!memcmp(buf1.data(), buf2.data(), bufSize));
}
void KisPaintDeviceTest::testReadBytesWrapAround()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
const int pixelSize = dev->pixelSize();
{
QRect readRect(10, 10, 20, 20);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final1.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check weird case when the read rect is larger than wrap rect
QRect readRect(10, 10, 30, 30);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final2.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// even more large
QRect readRect(10, 10, 40, 40);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final3.png");
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap rect contains the read rect entirely
QRect readRect(1, 1, 10, 10);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final4.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap happens only on vertical side of the rect
QRect readRect(1, 1, 29, 10);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final5.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (21 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (22 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
{
// check if the wrap happens only on horizontal side of the rect
QRect readRect(1, 1, 10, 29);
QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
readRect.height() *
pixelSize]);
dev->readBytes(buf.data(), readRect);
//dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final6.png");
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 21) * pixelSize, c1.data(), pixelSize));
QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 22) * pixelSize, c1.data(), pixelSize));
checkReadWriteRoundTrip(dev, readRect);
}
}
#include "kis_random_accessor_ng.h"
void KisPaintDeviceTest::testWrappedRandomAccessor()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
const int pixelSize = dev->pixelSize();
int x;
int y;
x = 3;
y = 3;
KisRandomAccessorSP dstIt = dev->createRandomAccessorNG(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 23;
y = 23;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 3;
y = 23;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 23;
y = 3;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = -17;
y = 3;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = 3;
y = -17;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
x = -17;
y = -17;
dstIt->moveTo(x, y);
QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
QCOMPARE(dstIt->numContiguousColumns(x), 17);
QCOMPARE(dstIt->numContiguousRows(y), 17);
}
#include "kis_iterator_ng.h"
static bool nextRowGeneral(KisHLineIteratorSP it, int y, const QRect &rc) {
it->nextRow();
return y < rc.height();
}
static bool nextRowGeneral(KisVLineIteratorSP it, int y, const QRect &rc) {
it->nextColumn();
return y < rc.width();
}
template <class T>
bool checkXY(const QPoint &pt, const QPoint &realPt) {
Q_UNUSED(pt);
Q_UNUSED(realPt);
return false;
}
template <>
bool checkXY<KisHLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
return pt == realPt;
}
template <>
bool checkXY<KisVLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
return pt.x() == realPt.y() && pt.y() == realPt.x();
}
#include <kis_wrapped_rect.h>
template <class T>
bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
Q_UNUSED(value);
Q_UNUSED(pt);
Q_UNUSED(wrappedRect);
return false;
}
template <>
bool checkConseqPixels<KisHLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
int x = KisWrappedRect::xToWrappedX(pt.x(), wrappedRect.wrapRect());
int borderX = wrappedRect.originalRect().x() + wrappedRect.wrapRect().width();
int conseq = x >= borderX ? wrappedRect.wrapRect().right() - x + 1 : borderX - x;
conseq = qMin(conseq, wrappedRect.originalRect().right() - pt.x() + 1);
return value == conseq;
}
template <>
bool checkConseqPixels<KisVLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
Q_UNUSED(pt);
Q_UNUSED(wrappedRect);
return value == 1;
}
template <class IteratorSP>
IteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) {
Q_UNUSED(dev);
Q_UNUSED(rc);
return 0;
}
template <>
KisHLineIteratorSP createIterator(KisPaintDeviceSP dev,
const QRect &rc) {
return dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
}
template <>
KisVLineIteratorSP createIterator(KisPaintDeviceSP dev,
const QRect &rc) {
return dev->createVLineIteratorNG(rc.x(), rc.y(), rc.height());
}
template <class IteratorSP>
void testWrappedLineIterator(QString testName, const QRect &rect)
{
testName = QString("%1_%2_%3_%4_%5")
.arg(testName)
.arg(rect.x())
.arg(rect.y())
.arg(rect.width())
.arg(rect.height());
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
// test rect fits the wrap rect in both dimensions
IteratorSP it = createIterator<IteratorSP>(dev, rect);
int y = 0;
do {
int x = 0;
do {
quint8 *data = it->rawData();
data[0] = 10 * x;
data[1] = 10 * y;
data[2] = 0;
data[3] = 255;
x++;
} while (it->nextPixel());
} while (nextRowGeneral(it, ++y, rect));
QRect rc = dev->defaultBounds()->bounds() | dev->exactBounds();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators", testName));
}
template <class IteratorSP>
void testWrappedLineIterator(const QString &testName)
{
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,20));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,20));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,10));
testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,10));
testWrappedLineIterator<IteratorSP>(testName, QRect(0,0,20,20));
}
void KisPaintDeviceTest::testWrappedHLineIterator()
{
testWrappedLineIterator<KisHLineIteratorSP>("hline_iterator");
}
void KisPaintDeviceTest::testWrappedVLineIterator()
{
testWrappedLineIterator<KisVLineIteratorSP>("vline_iterator");
}
template <class IteratorSP>
void testWrappedLineIteratorReadMoreThanBounds(QString testName)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
// fill device with a gradient
QRect bounds = dev->defaultBounds()->bounds();
for (int y = bounds.y(); y < bounds.y() + bounds.height(); y++) {
for (int x = bounds.x(); x < bounds.x() + bounds.width(); x++) {
QColor c((10 * x) % 255, (10 * y) % 255, 0, 255);
dev->setPixel(x, y, c);
}
}
// test rect doesn't fit the wrap rect in both dimentions
const QRect &rect(bounds.adjusted(-6,-6,8,8));
KisRandomAccessorSP dstIt = dst->createRandomAccessorNG(rect.x(), rect.y());
IteratorSP it = createIterator<IteratorSP>(dev, rect);
for (int y = rect.y(); y < rect.y() + rect.height(); y++) {
for (int x = rect.x(); x < rect.x() + rect.width(); x++) {
quint8 *data = it->rawData();
QVERIFY(checkConseqPixels<IteratorSP>(it->nConseqPixels(), QPoint(x, y), KisWrappedRect(rect, bounds)));
dstIt->moveTo(x, y);
memcpy(dstIt->rawData(), data, cs->pixelSize());
QVERIFY(checkXY<IteratorSP>(QPoint(it->x(), it->y()), QPoint(x,y)));
bool stepDone = it->nextPixel();
QCOMPARE(stepDone, x < rect.x() + rect.width() - 1);
}
if (!nextRowGeneral(it, y, rect)) break;
}
testName = QString("%1_%2_%3_%4_%5")
.arg(testName)
.arg(rect.x())
.arg(rect.y())
.arg(rect.width())
.arg(rect.height());
QRect rc = rect;
QImage result = dst->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QImage ref = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators_huge", testName, 1));
}
void KisPaintDeviceTest::testWrappedHLineIteratorReadMoreThanBounds()
{
testWrappedLineIteratorReadMoreThanBounds<KisHLineIteratorSP>("hline_iterator");
}
void KisPaintDeviceTest::testWrappedVLineIteratorReadMoreThanBounds()
{
testWrappedLineIteratorReadMoreThanBounds<KisVLineIteratorSP>("vline_iterator");
}
void KisPaintDeviceTest::testMoveWrapAround()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
KoColor c1(Qt::red, cs);
KoColor c2(Qt::green, cs);
dev->setPixel(3, 3, c1);
dev->setPixel(18, 18, c2);
// QRect rc = dev->defaultBounds()->bounds();
//dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move0.png");
QCOMPARE(dev->exactBounds(), QRect(3,3,16,16));
dev->move(QPoint(10,10));
QCOMPARE(dev->exactBounds(), QRect(8,8,6,6));
//dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move1.png");
}
#include "kis_lock_free_cache.h"
#define NUM_TYPES 3
// high-concurrency
#define NUM_CYCLES 500000
#define NUM_THREADS 4
struct TestingCache : KisLockFreeCache<int> {
int calculateNewValue() const {
return m_realValue;
}
QAtomicInt m_realValue;
};
class CacheStressJob : public QRunnable
{
public:
CacheStressJob(TestingCache &cache)
: m_cache(cache),
m_oldValue(0)
{
}
void run() {
for(qint32 i = 0; i < NUM_CYCLES; i++) {
qint32 type = i % NUM_TYPES;
switch(type) {
case 0:
m_cache.m_realValue.ref();
m_oldValue = m_cache.m_realValue;
m_cache.invalidate();
break;
case 1:
{
int newValue = m_cache.getValue();
Q_ASSERT(newValue >= m_oldValue);
Q_UNUSED(newValue);
}
break;
case 3:
QTest::qSleep(3);
break;
}
}
}
private:
TestingCache &m_cache;
int m_oldValue;
};
void KisPaintDeviceTest::testCacheState()
{
TestingCache cache;
QList<CacheStressJob*> jobsList;
CacheStressJob *job;
for(qint32 i = 0; i < NUM_THREADS; i++) {
//job = new CacheStressJob(value, cacheValue, cacheState);
job = new CacheStressJob(cache);
job->setAutoDelete(true);
jobsList.append(job);
}
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
foreach(job, jobsList) {
pool.start(job);
}
pool.waitForDone();
}
QTEST_KDEMAIN(KisPaintDeviceTest, GUI)
diff --git a/krita/image/tests/kis_paint_information_test.cpp b/krita/image/tests/kis_paint_information_test.cpp
index b7e259e4920..a4147121316 100644
--- a/krita/image/tests/kis_paint_information_test.cpp
+++ b/krita/image/tests/kis_paint_information_test.cpp
@@ -1,59 +1,59 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paint_information_test.h"
#include <qtest_kde.h>
#include "kis_paint_information.h"
#include <QDomDocument>
#include <Eigen/Core>
-#include <kdebug.h>
+#include <kis_debug.h>
void KisPaintInformationTest::testCreation()
{
KisPaintInformation test;
}
void KisPaintInformationTest::testSerialisation()
{
KisPaintInformation test(QPointF(double(rand()) / RAND_MAX, double(rand()) / RAND_MAX), double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX);
QDomDocument doc = QDomDocument("pi");
QDomElement root = doc.createElement("pi");
doc.appendChild(root);
test.toXML(doc, root);
KisPaintInformation testUnS = KisPaintInformation::fromXML(root);
QCOMPARE(test.pos().x() , testUnS.pos().x());
QCOMPARE(test.pos().y() , testUnS.pos().y());
QCOMPARE(test.pressure() , testUnS.pressure());
QCOMPARE(test.xTilt() , testUnS.xTilt());
QCOMPARE(test.yTilt() , testUnS.yTilt());
QCOMPARE(test.rotation() , testUnS.rotation());
QCOMPARE(test.tangentialPressure() , testUnS.tangentialPressure());
/**
* drawingAngle(), velocity() and distance() are calculated basing
* on the KisDistanceInformation data and are not available without
* it
*/
}
QTEST_KDEMAIN(KisPaintInformationTest, GUI)
diff --git a/krita/image/tests/kis_painter_test.cpp b/krita/image/tests/kis_painter_test.cpp
index 875e42f1d0e..f3151c2a34f 100644
--- a/krita/image/tests/kis_painter_test.cpp
+++ b/krita/image/tests/kis_painter_test.cpp
@@ -1,522 +1,522 @@
/*
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_painter_test.h"
#include <qtest_kde.h>
#include <kis_debug.h>
#include <QRect>
#include <QTime>
#include <QtXml>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include "kis_datamanager.h"
#include "kis_types.h"
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_pixel_selection.h"
#include "kis_fill_painter.h"
#include <kis_fixed_paint_device.h>
#include "testutil.h"
#include <kis_iterator_ng.h>
void KisPainterTest::allCsApplicator(void (KisPainterTest::* funcPtr)(const KoColorSpace*cs))
{
QList<const KoColorSpace*> colorsapces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile);
foreach(const KoColorSpace* cs, colorsapces) {
QString csId = cs->id();
// ALL THESE COLORSPACES ARE BROKEN: WE NEED UNITTESTS FOR COLORSPACES!
if (csId.startsWith("KS")) continue;
if (csId.startsWith("Xyz")) continue;
if (csId.startsWith('Y')) continue;
if (csId.contains("AF")) continue;
if (csId == "GRAYU16") continue; // No point in testing bounds with a cs without alpha
if (csId == "GRAYU8") continue; // No point in testing bounds with a cs without alpha
- qDebug() << "Testing with cs" << csId;
+ dbgKrita << "Testing with cs" << csId;
if (cs && cs->compositeOp(COMPOSITE_OVER) != 0) {
(this->*funcPtr)(cs);
} else {
- qDebug() << "Cannot bitBlt for cs" << csId;
+ dbgKrita << "Cannot bitBlt for cs" << csId;
}
}
}
void KisPainterTest::testSimpleBlt(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(20, 20, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(20, 20, 20, 20));
const KoCompositeOp* op;
{
op = cs->compositeOp(COMPOSITE_OVER);
KisPainter painter(dst);
painter.setCompositeOp(op);
painter.bitBlt(50, 50, src, 20, 20, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(50,50,20,20));
}
dst->clear();
{
op = cs->compositeOp(COMPOSITE_COPY);
KisPainter painter(dst);
painter.setCompositeOp(op);
painter.bitBlt(50, 50, src, 20, 20, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(50,50,20,20));
}
}
void KisPainterTest::testSimpleBlt()
{
allCsApplicator(&KisPainterTest::testSimpleBlt);
}
/*
Note: the bltSelection tests assume the following geometry:
0,0 0,30
+---------+------+
| 10,10 | |
| +----+ |
| |####| |
| |####| |
+----+----+ |
| 20,20 |
| |
| |
+----------------+
30,30
*/
void KisPainterTest::testPaintDeviceBltSelection(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(0, 0, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisSelectionSP selection = new KisSelection();
selection->pixelSelection()->select(QRect(10, 10, 20, 20));
selection->updateProjection();
QCOMPARE(selection->selectedExactRect(), QRect(10, 10, 20, 20));
KisPainter painter(dst);
painter.setSelection(selection);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QImage image = dst->convertToQImage(0);
image.save("blt_Selection_" + cs->name() + ".png");
QCOMPARE(dst->exactBounds(), QRect(10, 10, 10, 10));
const KoCompositeOp* op = cs->compositeOp(COMPOSITE_SUBTRACT);
if (op->id() == COMPOSITE_SUBTRACT) {
KisPaintDeviceSP dst2 = new KisPaintDevice(cs);
KisPainter painter2(dst2);
painter2.setSelection(selection);
painter2.setCompositeOp(op);
painter2.bitBlt(0, 0, src, 0, 0, 30, 30);
painter2.end();
QCOMPARE(dst2->exactBounds(), QRect(10, 10, 10, 10));
}
}
void KisPainterTest::testPaintDeviceBltSelection()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelection);
}
void KisPainterTest::testPaintDeviceBltSelectionIrregular(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisFillPainter gc(src);
gc.fillRect(0, 0, 20, 20, KoColor(Qt::red, cs));
gc.end();
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP psel = sel->pixelSelection();
psel->select(QRect(10, 15, 20, 15));
psel->select(QRect(15, 10, 15, 5));
QCOMPARE(psel->selectedExactRect(), QRect(10, 10, 20, 20));
QCOMPARE(TestUtil::alphaDevicePixel(psel, 13, 13), MIN_SELECTED);
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QImage image = dst->convertToQImage(0);
image.save("blt_Selection_irregular" + cs->name() + ".png");
QCOMPARE(dst->exactBounds(), QRect(10, 10, 10, 10));
foreach(KoChannelInfo * channel, cs->channels()) {
// Only compare alpha if there actually is an alpha channel in
// this colorspace
if (channel->channelType() == KoChannelInfo::ALPHA) {
QColor c;
dst->pixel(13, 13, &c);
QCOMPARE((int) c.alpha(), (int) OPACITY_TRANSPARENT_U8);
}
}
}
void KisPainterTest::testPaintDeviceBltSelectionIrregular()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelectionIrregular);
}
void KisPainterTest::testPaintDeviceBltSelectionInverted(const KoColorSpace * cs)
{
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisFillPainter gc(src);
gc.fillRect(0, 0, 30, 30, KoColor(Qt::red, cs));
gc.end();
QCOMPARE(src->exactBounds(), QRect(0, 0, 30, 30));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP psel = sel->pixelSelection();
psel->select(QRect(10, 10, 20, 20));
psel->invert();
sel->updateProjection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(0, 0, 30, 30));
}
void KisPainterTest::testPaintDeviceBltSelectionInverted()
{
allCsApplicator(&KisPainterTest::testPaintDeviceBltSelectionInverted);
}
void KisPainterTest::testSelectionBltSelection()
{
KisPixelSelectionSP src = new KisPixelSelection();
src->select(QRect(0, 0, 20, 20));
QCOMPARE(src->selectedExactRect(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP Selection = sel->pixelSelection();
Selection->select(QRect(10, 10, 20, 20));
QCOMPARE(Selection->selectedExactRect(), QRect(10, 10, 20, 20));
sel->updateProjection();
KisPixelSelectionSP dst = new KisPixelSelection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->selectedExactRect(), QRect(10, 10, 10, 10));
KisSequentialConstIterator it(dst, QRect(10, 10, 10, 10));
do {
// These are selections, so only one channel and it should
// be totally selected
QCOMPARE(it.oldRawData()[0], MAX_SELECTED);
} while (it.nextPixel());
}
/*
Test with non-square selection
0,0 0,30
+-----------+------+
| 13,13 | |
| x +--+ |
| +--+##| |
| |#####| |
+-----+-----+ |
| 20,20 |
| |
| |
+------------------+
30,30
*/
void KisPainterTest::testSelectionBltSelectionIrregular()
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPixelSelectionSP src = new KisPixelSelection();
src->select(QRect(0, 0, 20, 20));
QCOMPARE(src->selectedExactRect(), QRect(0, 0, 20, 20));
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP Selection = sel->pixelSelection();
Selection->select(QRect(10, 15, 20, 15));
Selection->select(QRect(15, 10, 15, 5));
QCOMPARE(Selection->selectedExactRect(), QRect(10, 10, 20, 20));
QCOMPARE(TestUtil::alphaDevicePixel(Selection, 13, 13), MIN_SELECTED);
sel->updateProjection();
KisPixelSelectionSP dst = new KisPixelSelection();
KisPainter painter(dst);
painter.setSelection(sel);
painter.bitBlt(0, 0, src, 0, 0, 30, 30);
painter.end();
QCOMPARE(dst->selectedExactRect(), QRect(10, 10, 10, 10));
QCOMPARE(TestUtil::alphaDevicePixel(dst, 13, 13), MIN_SELECTED);
}
void KisPainterTest::testSelectionBitBltFixedSelection()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
c.setOpacity(quint8(128));
src->fill(0, 0, 20, 20, c.data());
QCOMPARE(src->exactBounds(), QRect(0, 0, 20, 20));
KisFixedPaintDeviceSP fixedSelection = new KisFixedPaintDevice(cs);
fixedSelection->setRect(QRect(0, 0, 20, 20));
fixedSelection->initialize();
KoColor fill(Qt::white, cs);
fixedSelection->fill(5, 5, 10, 10, fill.data());
fixedSelection->convertTo(KoColorSpaceRegistry::instance()->alpha8());
KisPainter painter(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 20, 20);
painter.end();
QCOMPARE(dst->exactBounds(), QRect(5, 5, 10, 10));
/*
-qDebug() << "canary1.5";
+dbgKrita << "canary1.5";
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 10, 20);
painter.end();
-qDebug() << "canary2";
+dbgKrita << "canary2";
QCOMPARE(dst->exactBounds(), QRect(5, 5, 5, 10));
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(0, 0, src, fixedSelection, 5, 5, 5, 5, 10, 20);
painter.end();
-qDebug() << "canary3";
+dbgKrita << "canary3";
QCOMPARE(dst->exactBounds(), QRect(5, 5, 5, 10));
dst->clear();
painter.begin(dst);
painter.bitBltWithFixedSelection(5, 5, src, fixedSelection, 10, 20);
painter.end();
-qDebug() << "canary4";
+dbgKrita << "canary4";
QCOMPARE(dst->exactBounds(), QRect(10, 10, 5, 10));
*/
}
void KisPainterTest::testSelectionBitBltEraseCompositeOp()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KoColor c(Qt::red, cs);
dst->fill(0, 0, 150, 150, c.data());
KisPaintDeviceSP src = new KisPaintDevice(cs);
KoColor c2(Qt::black, cs);
src->fill(50, 50, 50, 50, c2.data());
KisSelectionSP sel = new KisSelection();
KisPixelSelectionSP selection = sel->pixelSelection();
selection->select(QRect(25, 25, 100, 100));
sel->updateProjection();
const KoCompositeOp* op = cs->compositeOp(COMPOSITE_ERASE);
KisPainter painter(dst);
painter.setSelection(sel);
painter.setCompositeOp(op);
painter.bitBlt(0, 0, src, 0, 0, 150, 150);
painter.end();
//dst->convertToQImage(0).save("result.png");
QRect erasedRect(50, 50, 50, 50);
KisSequentialConstIterator it(dst, QRect(0, 0, 150, 150));
do {
if(!erasedRect.contains(it.x(), it.y())) {
QVERIFY(memcmp(it.oldRawData(), c.data(), cs->pixelSize()) == 0);
}
} while (it.nextPixel());
}
void KisPainterTest::testSimpleAlphaCopy()
{
KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
quint8 p = 128;
src->fill(0, 0, 100, 100, &p);
QVERIFY(src->exactBounds() == QRect(0, 0, 100, 100));
KisPainter gc(dst);
gc.setCompositeOp(KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_COPY));
gc.bitBlt(QPoint(0, 0), src, src->exactBounds());
gc.end();
QCOMPARE(dst->exactBounds(), QRect(0, 0, 100, 100));
}
void KisPainterTest::checkPerformance()
{
KisPaintDeviceSP src = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
KisPaintDeviceSP dst = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
quint8 p = 128;
src->fill(0, 0, 10000, 5000, &p);
KisSelectionSP sel = new KisSelection();
sel->pixelSelection()->select(QRect(0, 0, 10000, 5000), 128);
sel->updateProjection();
QTime t;
t.start();
for (int i = 0; i < 10; ++i) {
KisPainter gc(dst);
gc.bitBlt(0, 0, src, 0, 0, 10000, 5000);
}
t.restart();
for (int i = 0; i < 10; ++i) {
KisPainter gc(dst, sel);
gc.bitBlt(0, 0, src, 0, 0, 10000, 5000);
}
}
void KisPainterTest::testBitBltOldData()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
quint8 defaultPixel = 0;
quint8 p1 = 128;
quint8 p2 = 129;
quint8 p3 = 130;
KoColor defaultColor(&defaultPixel, cs);
KoColor color1(&p1, cs);
KoColor color2(&p2, cs);
KoColor color3(&p3, cs);
QRect fillRect(0,0,5000,5000);
src->fill(fillRect, color1);
KisPainter srcGc(src);
srcGc.beginTransaction();
src->fill(fillRect, color2);
KisPainter dstGc(dst);
dstGc.bitBltOldData(QPoint(), src, fillRect);
QVERIFY(TestUtil::checkAlphaDeviceFilledWithPixel(dst, fillRect, p1));
dstGc.end();
srcGc.deleteTransaction();
}
void KisPainterTest::benchmarkBitBlt()
{
quint8 p = 128;
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KoColor color(&p, cs);
QRect fillRect(0,0,5000,5000);
src->fill(fillRect, color);
QBENCHMARK {
KisPainter gc(dst);
gc.bitBlt(QPoint(), src, fillRect);
}
}
void KisPainterTest::benchmarkBitBltOldData()
{
quint8 p = 128;
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
KisPaintDeviceSP src = new KisPaintDevice(cs);
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KoColor color(&p, cs);
QRect fillRect(0,0,5000,5000);
src->fill(fillRect, color);
QBENCHMARK {
KisPainter gc(dst);
gc.bitBltOldData(QPoint(), src, fillRect);
}
}
QTEST_KDEMAIN(KisPainterTest, NoGUI)
diff --git a/krita/image/tests/kis_pattern_test.cpp b/krita/image/tests/kis_pattern_test.cpp
index f60efb22185..a0c0ef8f816 100644
--- a/krita/image/tests/kis_pattern_test.cpp
+++ b/krita/image/tests/kis_pattern_test.cpp
@@ -1,71 +1,73 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_pattern_test.h"
#include <qtest_kde.h>
#include "KoPattern.h"
#include <QCryptographicHash>
#include <QByteArray>
+#include <kis_debug.h>
+
void KoPatternTest::testCreation()
{
KoPattern test(QString(FILES_DATA_DIR) + QDir::separator() + "pattern.pat");
}
void KoPatternTest::testRoundTripMd5()
{
QString filename(QString(FILES_DATA_DIR) + QDir::separator() + "test_pattern.png");
QString patFilename("test_pattern.pat");
KoPattern pngPattern(filename);
QVERIFY(pngPattern.load());
- qDebug() << "PNG Name:" << pngPattern.name();
- qDebug() << "PNG Filename:" << pngPattern.filename();
+ dbgKrita << "PNG Name:" << pngPattern.name();
+ dbgKrita << "PNG Filename:" << pngPattern.filename();
pngPattern.setFilename(patFilename);
pngPattern.save();
KoPattern patPattern(patFilename);
QVERIFY(patPattern.load());
- qDebug() << "PAT Name:" << patPattern.name();
- qDebug() << "PAT Filename:" << patPattern.filename();
+ dbgKrita << "PAT Name:" << patPattern.name();
+ dbgKrita << "PAT Filename:" << patPattern.filename();
- qDebug() << pngPattern.pattern().format();
- qDebug() << patPattern.pattern().format();
+ dbgKrita << pngPattern.pattern().format();
+ dbgKrita << patPattern.pattern().format();
QCOMPARE(pngPattern.pattern().convertToFormat(QImage::Format_ARGB32), patPattern.pattern().convertToFormat(QImage::Format_ARGB32));
QImage im1 = pngPattern.pattern().convertToFormat(QImage::Format_ARGB32);
QImage im2 = patPattern.pattern().convertToFormat(QImage::Format_ARGB32);
QCryptographicHash h1(QCryptographicHash::Md5);
h1.addData(QByteArray::fromRawData((const char*)im1.constBits(), im1.byteCount()));
QCryptographicHash h2(QCryptographicHash::Md5);
h2.addData(QByteArray::fromRawData((const char*)im2.constBits(), im2.byteCount()));
QCOMPARE(h1.result(), h2.result());
QCOMPARE(im1, im2);
QCOMPARE(pngPattern.md5(), patPattern.md5());
}
QTEST_KDEMAIN(KoPatternTest, GUI)
diff --git a/krita/image/tests/kis_processing_applicator_test.cpp b/krita/image/tests/kis_processing_applicator_test.cpp
index 4412a623a2f..431f3325ad4 100644
--- a/krita/image/tests/kis_processing_applicator_test.cpp
+++ b/krita/image/tests/kis_processing_applicator_test.cpp
@@ -1,210 +1,210 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_processing_applicator_test.h"
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_paint_device.h"
#include "kis_undo_stores.h"
#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
#include "kis_image.h"
#include "testutil.h"
/*
+----------+
|root |
| paint 2 |
| paint 1 |
+----------+
*/
KisImageSP createImage(KisUndoStore *undoStore,
KisPaintLayerSP &paintLayer1,
KisPaintLayerSP &paintLayer2)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, 300, 300, cs, "test");
QRect fillRect1(50,50,100,100);
QRect fillRect2(75,75,50,50);
paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
paintLayer1->paintDevice()->fill(fillRect1, KoColor(Qt::white, cs));
paintLayer2->paintDevice()->fill(fillRect2, KoColor(Qt::red, cs));
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
image->initialRefreshGraph();
return image;
}
bool checkLayers(KisImageWSP image,
const QString &prefix)
{
KisNodeSP layer1 = image->rootLayer()->firstChild();
KisNodeSP layer2 = layer1->nextSibling();
QVector<QImage> images(3);
images[0] = image->projection()->convertToQImage(0, 0, 0, 300, 300);
images[1] = layer1->paintDevice()->convertToQImage(0, 0, 0, 300, 300);
images[2] = layer2->paintDevice()->convertToQImage(0, 0, 0, 300, 300);
QVector<QString> names(3);
names[0] = QString("applicator_") + prefix + "_projection.png";
names[1] = QString("applicator_") + prefix + "_layer1.png";
names[2] = QString("applicator_") + prefix + "_layer2.png";
bool valid = true;
for(int i = 0; i < 3; i++) {
QImage ref(QString(FILES_DATA_DIR) + QDir::separator() +
"applicator" + QDir::separator() + names[i]);
QPoint temp;
if(!TestUtil::compareQImages(temp, ref, images[i], 1)) {
- qDebug() << "--- Wrong image:" << names[i];
+ dbgKrita << "--- Wrong image:" << names[i];
valid = false;
images[i].save(QString(FILES_OUTPUT_DIR) + QDir::separator() + names[i]);
}
}
return valid;
}
void KisProcessingApplicatorTest::testNonRecursiveProcessing()
{
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisPaintLayerSP paintLayer1;
KisPaintLayerSP paintLayer2;
KisImageSP image = createImage(undoStore, paintLayer1, paintLayer2);
QRect cropRect1(25,25,75,75);
QRect cropRect2(100,100,50,50);
QVERIFY(checkLayers(image, "initial"));
{
KisProcessingApplicator applicator(image, paintLayer1,
KisProcessingApplicator::NONE);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(cropRect1, true, false);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
}
QVERIFY(checkLayers(image, "crop_l1"));
{
KisProcessingApplicator applicator(image, paintLayer2,
KisProcessingApplicator::NONE);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(cropRect2, true, false);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
}
QVERIFY(checkLayers(image, "crop_l2"));
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayers(image, "crop_l1"));
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayers(image, "initial"));
}
void KisProcessingApplicatorTest::testRecursiveProcessing()
{
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisPaintLayerSP paintLayer1;
KisPaintLayerSP paintLayer2;
KisImageSP image = createImage(undoStore, paintLayer1, paintLayer2);
QRect cropRect1(40,40,86,86);
QVERIFY(checkLayers(image, "recursive_initial"));
{
KisProcessingApplicator applicator(image, image->rootLayer(),
KisProcessingApplicator::RECURSIVE);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(cropRect1, true, true);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
}
QVERIFY(checkLayers(image, "recursive_crop"));
undoStore->undo();
image->waitForDone();
QVERIFY(checkLayers(image, "recursive_initial"));
}
void KisProcessingApplicatorTest::testNoUIUpdates()
{
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisPaintLayerSP paintLayer1;
KisPaintLayerSP paintLayer2;
KisImageSP image = createImage(undoStore, paintLayer1, paintLayer2);
QSignalSpy uiSignalsCounter(image.data(), SIGNAL(sigImageUpdated(const QRect&)));
QRect cropRect1(40,40,86,86);
{
KisProcessingApplicator applicator(image, image->rootLayer(),
KisProcessingApplicator::RECURSIVE |
KisProcessingApplicator::NO_UI_UPDATES);
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(cropRect1, true, true);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
}
QCOMPARE(uiSignalsCounter.size(), 0);
uiSignalsCounter.clear();
undoStore->undo();
image->waitForDone();
QCOMPARE(uiSignalsCounter.size(), 0);
}
QTEST_KDEMAIN(KisProcessingApplicatorTest, GUI)
diff --git a/krita/image/tests/kis_processings_test.cpp b/krita/image/tests/kis_processings_test.cpp
index 331d8dda57a..da2a99f0d32 100644
--- a/krita/image/tests/kis_processings_test.cpp
+++ b/krita/image/tests/kis_processings_test.cpp
@@ -1,117 +1,117 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_processings_test.h"
#include <qtest_kde.h>
#include "kis_undo_stores.h"
#include "kis_processing_applicator.h"
#include "processing/kis_crop_processing_visitor.h"
#include "testutil.h"
#include "qimage_based_test.h"
#include "kis_filter_strategy.h"
#include "kis_transform_worker.h"
#include "processing/kis_transform_processing_visitor.h"
class BaseProcessingTest : public TestUtil::QImageBasedTest
{
public:
BaseProcessingTest()
: QImageBasedTest("processings")
{
}
void test(const QString &testname, KisProcessingVisitorSP visitor) {
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
KisImageSP image = createImage(undoStore);
image->initialRefreshGraph();
QVERIFY(checkLayersInitial(image));
KisProcessingApplicator applicator(image, image->root(),
KisProcessingApplicator::RECURSIVE);
applicator.applyVisitor(visitor);
applicator.end();
image->waitForDone();
/**
* NOTE: after a change in KisLayer::changeRect(), which now
* crops change rect for layers with COMPOSITE_COPY
* composition, the clone layer will have some ghost pixels
* outside main projection rect. That is ok, because these
* pixels will never be painted due to a Filter Layer above,
* which crops the change rect.
*/
QVERIFY(checkLayers(image, testname));
undoStore->undo();
image->waitForDone();
if (!checkLayersInitial(image)) {
- qWarning() << "NOTE: undo is not completely identical to the original image. Falling back to projection comparison";
+ warnKrita << "NOTE: undo is not completely identical to the original image. Falling back to projection comparison";
QVERIFY(checkLayersInitialRootOnly(image));
}
}
};
void KisProcessingsTest::testCropVisitor()
{
KisProcessingVisitorSP visitor =
new KisCropProcessingVisitor(QRect(45,45,410,410), true, true);
BaseProcessingTest tester;
tester.test("crop", visitor);
}
void KisProcessingsTest::testTransformVisitorScale()
{
BaseProcessingTest tester;
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(0.5, 0.5,
0,0,QPointF(),
0,
0,0,
filter);
tester.test("transform_scale", visitor);
}
void KisProcessingsTest::testTransformVisitorScaleRotate()
{
BaseProcessingTest tester;
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisProcessingVisitorSP visitor =
new KisTransformProcessingVisitor(0.5, 0.5,
0,0,QPointF(),
M_PI,
320,221,
filter);
tester.test("transform_scale_rotate", visitor);
}
QTEST_KDEMAIN(KisProcessingsTest, GUI)
diff --git a/krita/image/tests/kis_projection_leaf_test.cpp b/krita/image/tests/kis_projection_leaf_test.cpp
index 5ce6f843b48..75e0cf2199b 100644
--- a/krita/image/tests/kis_projection_leaf_test.cpp
+++ b/krita/image/tests/kis_projection_leaf_test.cpp
@@ -1,290 +1,290 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_projection_leaf_test.h"
#include <qtest_kde.h>
#include "qimage_based_test.h"
#include "kis_projection_leaf.h"
#include "kis_group_layer.h"
struct TestImage : TestUtil::QImageBasedTest {
TestImage() : TestUtil::QImageBasedTest("")
{
undoStore = new KisSurrogateUndoStore();
image = createImage(undoStore);
addGlobalSelection(image);
}
KisSurrogateUndoStore *undoStore;
KisImageSP image;
KisNodeSP findBlur1() {
return findNode(image->root(), "blur1");
}
KisNodeSP findClone1() {
return findNode(image->root(), "clone1");
}
KisNodeSP findPaint1() {
return findNode(image->root(), "paint1");
}
};
bool safeCompare(KisProjectionLeafSP leaf, KisNodeSP node)
{
return (!leaf && !node) || (leaf->node() == node);
}
void checkNode(KisNodeSP node, const QString &prefix)
{
- qDebug() << prefix << node->name();
+ dbgKrita << prefix << node->name();
safeCompare(node->projectionLeaf()->parent(), node->parent());
safeCompare(node->projectionLeaf()->firstChild(), node->firstChild());
safeCompare(node->projectionLeaf()->lastChild(), node->lastChild());
safeCompare(node->projectionLeaf()->prevSibling(), node->prevSibling());
safeCompare(node->projectionLeaf()->nextSibling(), node->nextSibling());
QCOMPARE(node->projectionLeaf()->node(), node);
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
checkNode(prevNode, QString("\"\"%1").arg(prefix));
prevNode = prevNode->prevSibling();
}
}
void printNodes(KisNodeSP node, const QString &prefix = "")
{
- qDebug() << prefix << node->name();
+ dbgKrita << prefix << node->name();
KisNodeSP prevNode = node->lastChild();
while(prevNode) {
printNodes(prevNode, QString("\"\"%1").arg(prefix));
prevNode = prevNode->prevSibling();
}
}
void printLeafsBackward(KisProjectionLeafSP leaf, QList<QString> &refNodes, const QString &prefix = "")
{
- qDebug() << prefix << leaf->node()->name();
+ dbgKrita << prefix << leaf->node()->name();
QCOMPARE(leaf->node()->name(), refNodes.takeFirst());
KisProjectionLeafSP prevLeaf = leaf->lastChild();
while(prevLeaf) {
printLeafsBackward(prevLeaf, refNodes, QString("\"\"%1").arg(prefix));
prevLeaf = prevLeaf->prevSibling();
}
if (prefix == "") {
QVERIFY(refNodes.isEmpty());
}
}
void printLeafsForward(KisProjectionLeafSP leaf, QList<QString> &refNodes, const QString &prefix = "")
{
- qDebug() << prefix << leaf->node()->name();
+ dbgKrita << prefix << leaf->node()->name();
QCOMPARE(leaf->node()->name(), refNodes.takeFirst());
KisProjectionLeafSP prevLeaf = leaf->firstChild();
while(prevLeaf) {
printLeafsForward(prevLeaf, refNodes, QString("\"\"%1").arg(prefix));
prevLeaf = prevLeaf->nextSibling();
}
}
void printParents(KisProjectionLeafSP leaf, QList<QString> &refNodes, const QString &prefix = "")
{
- qDebug() << prefix << leaf->node()->name();
+ dbgKrita << prefix << leaf->node()->name();
QCOMPARE(leaf->node()->name(), refNodes.takeFirst());
leaf = leaf->parent();
if (leaf) {
printParents(leaf, refNodes, QString("\"\"%1").arg(prefix));
}
}
void KisProjectionLeafTest::test()
{
TestImage t;
checkNode(t.image->root(), "");
}
void KisProjectionLeafTest::testPassThrough()
{
TestImage t;
KisGroupLayerSP group1 = new KisGroupLayer(t.image, "group1", OPACITY_OPAQUE_U8);
KisPaintLayerSP paint2 = new KisPaintLayer(t.image, "paint2", OPACITY_OPAQUE_U8);
KisPaintLayerSP paint3 = new KisPaintLayer(t.image, "paint3", OPACITY_OPAQUE_U8);
KisPaintLayerSP paint4 = new KisPaintLayer(t.image, "paint4", OPACITY_OPAQUE_U8);
group1->setPassThroughMode(true);
t.image->addNode(group1, t.image->root(), t.findBlur1());
t.image->addNode(paint2, group1);
t.image->addNode(paint3, group1);
t.image->addNode(paint4, group1);
//checkNode(t.image->root(), "");
- qDebug() << "== Nodes";
+ dbgKrita << "== Nodes";
printNodes(t.image->root());
{
- qDebug() << "== Leafs backward";
+ dbgKrita << "== Leafs backward";
QList<QString> refNodes;
refNodes << "root"
<< "selection"
<< "paint1"
<< "tmask1"
<< "group1" << "paint4" << "paint3" << "paint2"
<< "blur1"
<< "clone1";
printLeafsBackward(t.image->root()->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Leafs forward";
+ dbgKrita << "== Leafs forward";
QList<QString> refNodes;
refNodes << "root"
<< "clone1"
<< "blur1"
<< "paint2" << "paint3" << "paint4" << "group1"
<< "paint1"
<< "tmask1"
<< "selection";
printLeafsForward(t.image->root()->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for paint4";
+ dbgKrita << "== Parents for paint4";
QList<QString> refNodes;
refNodes << "paint4" << "root";
printParents(paint4->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for paint3";
+ dbgKrita << "== Parents for paint3";
QList<QString> refNodes;
refNodes << "paint3" << "root";
printParents(paint3->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for group1";
+ dbgKrita << "== Parents for group1";
QList<QString> refNodes;
refNodes << "group1" << "root";
printParents(group1->projectionLeaf(), refNodes);
}
}
void KisProjectionLeafTest::testNestedPassThrough()
{
TestImage t;
KisGroupLayerSP group1 = new KisGroupLayer(t.image, "group1", OPACITY_OPAQUE_U8);
KisGroupLayerSP group2 = new KisGroupLayer(t.image, "group2", OPACITY_OPAQUE_U8);
KisGroupLayerSP group3 = new KisGroupLayer(t.image, "group3", OPACITY_OPAQUE_U8);
KisPaintLayerSP paint4 = new KisPaintLayer(t.image, "paint4", OPACITY_OPAQUE_U8);
KisPaintLayerSP paint5 = new KisPaintLayer(t.image, "paint5", OPACITY_OPAQUE_U8);
group1->setPassThroughMode(true);
group2->setPassThroughMode(true);
group3->setPassThroughMode(true);
t.image->addNode(group1, t.image->root(), t.findBlur1());
t.image->addNode(group2, group1);
t.image->addNode(paint4, group2);
t.image->addNode(group3, t.image->root(), t.findBlur1());
t.image->addNode(paint5, group3);
//checkNode(t.image->root(), "");
- qDebug() << "== Nodes";
+ dbgKrita << "== Nodes";
printNodes(t.image->root());
{
- qDebug() << "== Leafs backward";
+ dbgKrita << "== Leafs backward";
QList<QString> refNodes;
refNodes << "root"
<< "selection"
<< "paint1"
<< "tmask1"
<< "group1" << "group2" <<"paint4"
<< "group3" << "paint5"
<< "blur1"
<< "clone1";
printLeafsBackward(t.image->root()->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Leafs forward";
+ dbgKrita << "== Leafs forward";
QList<QString> refNodes;
refNodes << "root"
<< "clone1"
<< "blur1"
<< "paint5" << "group3"
<< "paint4" << "group2" << "group1"
<< "paint1"
<< "tmask1"
<< "selection";
printLeafsForward(t.image->root()->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for paint4";
+ dbgKrita << "== Parents for paint4";
QList<QString> refNodes;
refNodes << "paint4" << "root";
printParents(paint4->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for paint5";
+ dbgKrita << "== Parents for paint5";
QList<QString> refNodes;
refNodes << "paint5" << "root";
printParents(paint5->projectionLeaf(), refNodes);
}
{
- qDebug() << "== Parents for group1";
+ dbgKrita << "== Parents for group1";
QList<QString> refNodes;
refNodes << "group1" << "root";
printParents(group1->projectionLeaf(), refNodes);
}
}
QTEST_KDEMAIN(KisProjectionLeafTest, GUI)
diff --git a/krita/image/tests/kis_scanline_fill_test.cpp b/krita/image/tests/kis_scanline_fill_test.cpp
index 6f3eec8e012..9c7818a974b 100644
--- a/krita/image/tests/kis_scanline_fill_test.cpp
+++ b/krita/image/tests/kis_scanline_fill_test.cpp
@@ -1,250 +1,250 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_scanline_fill_test.h"
#include "testutil.h"
#include <qtest_kde.h>
#include <floodfill/kis_scanline_fill.h>
#include <floodfill/kis_fill_interval.h>
#include <floodfill/kis_fill_interval_map.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_types.h"
#include "kis_paint_device.h"
void KisScanlineFillTest::testFillGeneral(const QVector<KisFillInterval> &initialBackwardIntervals,
const QVector<QColor> &expectedResult,
const QVector<KisFillInterval> &expectedForwardIntervals,
const QVector<KisFillInterval> &expectedBackwardIntervals)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->setPixel(1, 0, Qt::white);
dev->setPixel(2, 0, Qt::white);
dev->setPixel(5, 0, Qt::white);
dev->setPixel(8, 0, Qt::white);
dev->setPixel(17, 0, Qt::white);
QRect boundingRect(-10, -10, 30, 30);
KisScanlineFill gc(dev, QPoint(), boundingRect);
KisFillIntervalMap *backwardMap = gc.testingGetBackwardIntervals();
foreach(const KisFillInterval &i, initialBackwardIntervals) {
backwardMap->insertInterval(i);
}
KisFillInterval processInterval(0,10,0);
gc.testingProcessLine(processInterval);
Q_ASSERT(expectedResult.size() == processInterval.width());
for (int i = 0; i < 11; i++) {
QColor c;
dev->pixel(i, 0, &c);
- qDebug() << i << ":" << c.red();
+ dbgKrita << i << ":" << c.red();
QCOMPARE(c, expectedResult[i]);
}
QVector<KisFillInterval> forwardIntervals =
gc.testingGetForwardIntervals();
for (int i = 0; i < forwardIntervals.size(); i++) {
- qDebug() << "FW:" << forwardIntervals[i];
+ dbgKrita << "FW:" << forwardIntervals[i];
QCOMPARE(forwardIntervals[i], expectedForwardIntervals[i]);
}
QCOMPARE(forwardIntervals.size(), expectedForwardIntervals.size());
QStack<KisFillInterval> backwardIntervals =
gc.testingGetBackwardIntervals()->fetchAllIntervals();
for (int i = 0; i < backwardIntervals.size(); i++) {
- qDebug() << "BW:" << backwardIntervals[i];
+ dbgKrita << "BW:" << backwardIntervals[i];
QCOMPARE(backwardIntervals[i], expectedBackwardIntervals[i]);
}
QCOMPARE(backwardIntervals.size(), expectedBackwardIntervals.size());
}
inline QColor testingColor(quint8 c) {
return QColor(c, c, c, c);
}
void KisScanlineFillTest::testSimpleFill()
{
QVector<KisFillInterval> initialBackwardIntervals;
QVector<QColor> expectedResult;
expectedResult << testingColor(200); // 0
expectedResult << testingColor(255); // 1
expectedResult << testingColor(255); // 2
expectedResult << testingColor(200); // 3
expectedResult << testingColor(200); // 4
expectedResult << testingColor(255); // 5
expectedResult << testingColor(200); // 6
expectedResult << testingColor(200); // 7
expectedResult << testingColor(255); // 8
expectedResult << testingColor(200); // 9
expectedResult << testingColor(200); // 10
QVector<KisFillInterval> expectedForwardIntervals;
expectedForwardIntervals << KisFillInterval(-10, 0, 1);
expectedForwardIntervals << KisFillInterval(3, 4, 1);
expectedForwardIntervals << KisFillInterval(6, 7, 1);
expectedForwardIntervals << KisFillInterval(9, 16, 1);
QVector<KisFillInterval> expectedBackwardIntervals;
expectedBackwardIntervals << KisFillInterval(-10, -1, 0);
expectedBackwardIntervals << KisFillInterval(11, 16, 0);
testFillGeneral(initialBackwardIntervals,
expectedResult,
expectedForwardIntervals,
expectedBackwardIntervals);
}
void KisScanlineFillTest::testFillBackwardCollisionOnTheLeft()
{
QVector<KisFillInterval> initialBackwardIntervals;
initialBackwardIntervals << KisFillInterval(-10, 0, 0);
QVector<QColor> expectedResult;
expectedResult << testingColor( 0); // 0
expectedResult << testingColor(255); // 1
expectedResult << testingColor(255); // 2
expectedResult << testingColor(200); // 3
expectedResult << testingColor(200); // 4
expectedResult << testingColor(255); // 5
expectedResult << testingColor(200); // 6
expectedResult << testingColor(200); // 7
expectedResult << testingColor(255); // 8
expectedResult << testingColor(200); // 9
expectedResult << testingColor(200); // 10
QVector<KisFillInterval> expectedForwardIntervals;
expectedForwardIntervals << KisFillInterval(3, 4, 1);
expectedForwardIntervals << KisFillInterval(6, 7, 1);
expectedForwardIntervals << KisFillInterval(9, 16, 1);
QVector<KisFillInterval> expectedBackwardIntervals;
expectedBackwardIntervals << KisFillInterval(-10, -1, 0);
expectedBackwardIntervals << KisFillInterval(11, 16, 0);
testFillGeneral(initialBackwardIntervals,
expectedResult,
expectedForwardIntervals,
expectedBackwardIntervals);
}
void KisScanlineFillTest::testFillBackwardCollisionOnTheRight()
{
QVector<KisFillInterval> initialBackwardIntervals;
initialBackwardIntervals << KisFillInterval(9, 20, 0);
QVector<QColor> expectedResult;
expectedResult << testingColor(200); // 0
expectedResult << testingColor(255); // 1
expectedResult << testingColor(255); // 2
expectedResult << testingColor(200); // 3
expectedResult << testingColor(200); // 4
expectedResult << testingColor(255); // 5
expectedResult << testingColor(200); // 6
expectedResult << testingColor(200); // 7
expectedResult << testingColor(255); // 8
expectedResult << testingColor( 0); // 9
expectedResult << testingColor( 0); // 10
QVector<KisFillInterval> expectedForwardIntervals;
expectedForwardIntervals << KisFillInterval(-10, 0, 1);
expectedForwardIntervals << KisFillInterval(3, 4, 1);
expectedForwardIntervals << KisFillInterval(6, 7, 1);
QVector<KisFillInterval> expectedBackwardIntervals;
expectedBackwardIntervals << KisFillInterval(-10, -1, 0);
expectedBackwardIntervals << KisFillInterval(11, 20, 0);
testFillGeneral(initialBackwardIntervals,
expectedResult,
expectedForwardIntervals,
expectedBackwardIntervals);
}
void KisScanlineFillTest::testFillBackwardCollisionFull()
{
QVector<KisFillInterval> initialBackwardIntervals;
initialBackwardIntervals << KisFillInterval(-10, 20, 0);
QVector<QColor> expectedResult;
expectedResult << testingColor( 0); // 0
expectedResult << testingColor(255); // 1
expectedResult << testingColor(255); // 2
expectedResult << testingColor( 0); // 3
expectedResult << testingColor( 0); // 4
expectedResult << testingColor(255); // 5
expectedResult << testingColor( 0); // 6
expectedResult << testingColor( 0); // 7
expectedResult << testingColor(255); // 8
expectedResult << testingColor( 0); // 9
expectedResult << testingColor( 0); // 10
QVector<KisFillInterval> expectedForwardIntervals;
QVector<KisFillInterval> expectedBackwardIntervals;
expectedBackwardIntervals << KisFillInterval(-10, -1, 0);
expectedBackwardIntervals << KisFillInterval(11, 20, 0);
testFillGeneral(initialBackwardIntervals,
expectedResult,
expectedForwardIntervals,
expectedBackwardIntervals);
}
void KisScanlineFillTest::testFillBackwardCollisionSanityCheck()
{
#if defined ENABLE_FILL_SANITY_CHECKS && defined ENABLE_CHECKS_FOR_TESTING
QVector<KisFillInterval> initialBackwardIntervals;
initialBackwardIntervals << KisFillInterval(0, 10, 0);
QVector<QColor> expectedResult;
QVector<KisFillInterval> expectedForwardIntervals;
QVector<KisFillInterval> expectedBackwardIntervals;
expectedBackwardIntervals << KisFillInterval(0, 10, 0);
bool gotException = false;
try {
testFillGeneral(initialBackwardIntervals,
expectedResult,
expectedForwardIntervals,
expectedBackwardIntervals);
} catch (...) {
gotException = true;
}
QVERIFY(gotException);
#endif /* ENABLE_FILL_SANITY_CHECKS */
}
QTEST_KDEMAIN(KisScanlineFillTest, GUI)
diff --git a/krita/image/tests/kis_stroke_strategy_undo_command_based_test.cpp b/krita/image/tests/kis_stroke_strategy_undo_command_based_test.cpp
index d22d81d06a8..4ea90a5047f 100644
--- a/krita/image/tests/kis_stroke_strategy_undo_command_based_test.cpp
+++ b/krita/image/tests/kis_stroke_strategy_undo_command_based_test.cpp
@@ -1,179 +1,179 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_stroke_strategy_undo_command_based_test.h"
#include <qtest_kde.h>
#include <KoColorSpaceRegistry.h>
#include "kis_image.h"
#include "kis_stroke.h"
#include "kis_stroke_strategy_undo_command_based.h"
#include "scheduler_utils.h"
inline QString undoString(bool undo) {
return undo ? "_undo" : "_redo";
}
class TestingUndoCommand : public KUndo2Command
{
public:
TestingUndoCommand(const KUndo2MagicString &name, QString &result)
: KUndo2Command(name),
m_result(result)
{
}
void undo() {
m_result += QString(" ") + text().toString() + undoString(true);
}
void redo() {
m_result += QString(" ") + text().toString() + undoString(false);
}
private:
QString &m_result;
};
void KisStrokeStrategyUndoCommandBasedTest::testFinishedStroke()
{
QString result;
KUndo2CommandSP initCommand(new TestingUndoCommand(kundo2_noi18n("init"), result));
KUndo2CommandSP dabCommand(new TestingUndoCommand(kundo2_noi18n("dab"), result));
KUndo2CommandSP finishCommand(new TestingUndoCommand(kundo2_noi18n("finish"), result));
KisStrokeStrategy *strategy =
new KisStrokeStrategyUndoCommandBased(kundo2_noi18n("test"), false, 0,
initCommand, finishCommand);
KisStroke stroke(strategy);
stroke.addJob(
new KisStrokeStrategyUndoCommandBased::Data(dabCommand));
stroke.endStroke();
executeStrokeJobs(&stroke);
SCOMPARE(result.trimmed(), "init_redo dab_redo finish_redo");
}
void KisStrokeStrategyUndoCommandBasedTest::testCancelledStroke()
{
QString result;
KUndo2CommandSP initCommand(new TestingUndoCommand(kundo2_noi18n("init"), result));
KUndo2CommandSP dabCommand(new TestingUndoCommand(kundo2_noi18n("dab"), result));
KUndo2CommandSP finishCommand(new TestingUndoCommand(kundo2_noi18n("finish"), result));
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 300, 300, cs, "test", true);
KisStrokeStrategy *strategy =
new KisStrokeStrategyUndoCommandBased(kundo2_noi18n("test"), false,
image->postExecutionUndoAdapter(),
initCommand, finishCommand);
KisStrokeId id = image->startStroke(strategy);
image->addJob(id, new KisStrokeStrategyUndoCommandBased::Data(dabCommand));
QTest::qSleep(500);
image->cancelStroke(id);
image->waitForDone();
SCOMPARE(result.trimmed(), "init_redo dab_redo dab_undo init_undo");
}
#define NUM_JOBS 1000
#define SEQUENTIAL_NTH 12
#define NUM_CHECKS 10
#define CHECK_DELAY 2 // ms
class ExclusivenessCheckerCommand : public KUndo2Command
{
public:
ExclusivenessCheckerCommand(QAtomicInt &counter,
QAtomicInt &hadConcurrency,
bool exclusive)
: m_counter(counter),
m_hadConcurrency(hadConcurrency),
m_exclusive(exclusive)
{
}
void redo() { checkState(); }
void undo() { checkState(); }
private:
void checkState() {
m_counter.ref();
for(int i = 0; i < NUM_CHECKS; i++) {
if(m_exclusive) {
Q_ASSERT(m_counter == 1);
}
else {
m_hadConcurrency.ref();
}
QTest::qSleep(CHECK_DELAY);
}
m_counter.deref();
}
private:
QAtomicInt &m_counter;
QAtomicInt &m_hadConcurrency;
bool m_exclusive;
};
void KisStrokeStrategyUndoCommandBasedTest::stressTestSequentialCommands()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 300, 300, cs, "test", true);
QAtomicInt counter;
QAtomicInt hadConcurrency;
KisStrokeStrategy *strategy =
new KisStrokeStrategyUndoCommandBased(kundo2_noi18n("test"), false, 0);
KisStrokeId id = image->startStroke(strategy);
for(int i = 0; i < NUM_JOBS; i++) {
bool isSequential = i % SEQUENTIAL_NTH == 0;
KisStrokeJobData::Sequentiality seq = isSequential ?
KisStrokeJobData::SEQUENTIAL : KisStrokeJobData::CONCURRENT;
KUndo2CommandSP command(new ExclusivenessCheckerCommand(counter,
hadConcurrency,
isSequential));
image->addJob(id,
new KisStrokeStrategyUndoCommandBased::Data(command, seq));
}
image->endStroke(id);
image->waitForDone();
QVERIFY(!counter);
- qDebug() << "Concurrency observed:" << hadConcurrency
+ dbgKrita << "Concurrency observed:" << hadConcurrency
<< "/" << NUM_CHECKS * NUM_JOBS;
}
QTEST_KDEMAIN(KisStrokeStrategyUndoCommandBasedTest, NoGUI)
diff --git a/krita/image/tests/kis_transform_mask_test.cpp b/krita/image/tests/kis_transform_mask_test.cpp
index 8e74433114b..7d0baf7356c 100644
--- a/krita/image/tests/kis_transform_mask_test.cpp
+++ b/krita/image/tests/kis_transform_mask_test.cpp
@@ -1,988 +1,988 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transform_mask_test.h"
#include <qtest_kde.h>
#include <KoColor.h>
#include "kis_transform_mask.h"
#include "kis_transform_mask_params_interface.h"
#include "testutil.h"
#include "kis_algebra_2d.h"
#include "kis_safe_transform.h"
#include "kis_clone_layer.h"
#include "kis_group_layer.h"
inline QString toOctaveFormat(const QTransform &t)
{
QString s("T = [%1 %2 %3; %4 %5 %6; %7 %8 %9]");
s = s
.arg(t.m11()).arg(t.m12()).arg(t.m13())
.arg(t.m21()).arg(t.m22()).arg(t.m23())
.arg(t.m31()).arg(t.m32()).arg(t.m33());
return s;
}
void KisTransformMaskTest::testSafeTransform()
{
QTransform transform(-0.177454, -0.805953, -0.00213713,
-1.9295, -0.371835, -0.00290463,
3075.05, 2252.32, 7.62371);
QRectF testRect(0, 1024, 512, 512);
KisSafeTransform t2(transform, QRect(0, 0, 2048, 2048), testRect.toRect());
QPolygonF fwdPoly = t2.mapForward(testRect);
QRectF fwdRect = t2.mapRectForward(testRect);
QPolygonF bwdPoly = t2.mapBackward(fwdPoly);
QRectF bwdRect = t2.mapRectBackward(fwdRect);
QPolygon ref;
ref.clear();
ref << QPoint(284, 410);
ref << QPoint(10, 613);
ref << QPoint(35, 532);
ref << QPoint(236, 403);
ref << QPoint(284, 410);
QCOMPARE(fwdPoly.toPolygon(), ref);
QCOMPARE(fwdRect.toRect(), QRect(10,403,274,211));
ref.clear();
ref << QPoint(512, 1024);
ref << QPoint(512, 1536);
ref << QPoint(0, 1536);
ref << QPoint(0, 1024);
ref << QPoint(512, 1024);
QCOMPARE(bwdPoly.toPolygon(), ref);
QCOMPARE(bwdRect.toRect(), QRect(0, 994, 1198, 584));
/*
QImage image(2500, 2500, QImage::Format_ARGB32);
QPainter gc(&image);
gc.setPen(Qt::cyan);
gc.setOpacity(0.7);
gc.setBrush(Qt::red);
gc.drawPolygon(t2.srcClipPolygon());
gc.setBrush(Qt::green);
gc.drawPolygon(t2.dstClipPolygon());
- qDebug() << ppVar(testRect);
- qDebug() << ppVar(fwdPoly);
- qDebug() << ppVar(fwdRect);
- qDebug() << ppVar(bwdPoly);
- qDebug() << ppVar(bwdRect);
+ dbgKrita << ppVar(testRect);
+ dbgKrita << ppVar(fwdPoly);
+ dbgKrita << ppVar(fwdRect);
+ dbgKrita << ppVar(bwdPoly);
+ dbgKrita << ppVar(bwdRect);
gc.setBrush(Qt::yellow);
gc.drawPolygon(testRect);
gc.setBrush(Qt::red);
gc.drawPolygon(fwdRect);
gc.setBrush(Qt::blue);
gc.drawPolygon(fwdPoly);
gc.setBrush(Qt::magenta);
gc.drawPolygon(bwdRect);
gc.setBrush(Qt::cyan);
gc.drawPolygon(bwdPoly);
gc.end();
image.save("polygons_safety.png");
*/
}
void KisTransformMaskTest::testSafeTransformUnity()
{
QTransform transform;
QRectF testRect(0, 1024, 512, 512);
KisSafeTransform t2(transform, QRect(0, 0, 2048, 2048), testRect.toRect());
QPolygonF fwdPoly = t2.mapForward(testRect);
QRectF fwdRect = t2.mapRectForward(testRect);
QPolygonF bwdPoly = t2.mapBackward(fwdPoly);
QRectF bwdRect = t2.mapRectBackward(fwdRect);
QCOMPARE(testRect, fwdRect);
QCOMPARE(testRect, bwdRect);
QCOMPARE(fwdPoly, QPolygonF(testRect));
QCOMPARE(bwdPoly, QPolygonF(testRect));
}
void KisTransformMaskTest::testSafeTransformSingleVanishingPoint()
{
// rotation around 0X has a single vanishing point for 0Y axis
QTransform transform(1, 0, 0,
-0.870208, -0.414416, -0.000955222,
132.386, 1082.91, 1.99439);
QTransform R; R.rotateRadians(M_PI / 4.0);
//transform *= R;
QRectF testRect(1536, 1024, 512, 512);
KisSafeTransform t2(transform, QRect(0, 0, 2048, 2048), testRect.toRect());
QPolygonF fwdPoly = t2.mapForward(testRect);
QRectF fwdRect = t2.mapRectForward(testRect);
QPolygonF bwdPoly = t2.mapBackward(fwdPoly);
QRectF bwdRect = t2.mapRectBackward(fwdRect);
/**
* A special weird rect that crosses the vanishing point,
* which is (911.001, 433.84) in this case
*/
QRectF fwdNastyRect(800, 100, 400, 600);
//QRectF fwdNastyRect(100, 400, 1000, 800);
QRectF bwdNastyRect = t2.mapRectBackward(fwdNastyRect);
/*
- qDebug() << ppVar(testRect);
- qDebug() << ppVar(fwdPoly);
- qDebug() << ppVar(fwdRect);
- qDebug() << ppVar(bwdPoly);
- qDebug() << ppVar(bwdRect);
- qDebug() << ppVar(bwdNastyRect);
+ dbgKrita << ppVar(testRect);
+ dbgKrita << ppVar(fwdPoly);
+ dbgKrita << ppVar(fwdRect);
+ dbgKrita << ppVar(bwdPoly);
+ dbgKrita << ppVar(bwdRect);
+ dbgKrita << ppVar(bwdNastyRect);
*/
QPolygon ref;
ref.clear();
ref << QPoint(765,648);
ref << QPoint(1269, 648);
ref << QPoint(1601, 847);
ref << QPoint(629, 847);
ref << QPoint(765, 648);
QCOMPARE(fwdPoly.toPolygon(), ref);
QCOMPARE(fwdRect.toRect(), QRect(629,648,971,199));
ref.clear();
ref << QPoint(1536,1024);
ref << QPoint(2048,1024);
ref << QPoint(2048,1536);
ref << QPoint(1536,1536);
ref << QPoint(1536,1024);
QCOMPARE(bwdPoly.toPolygon(), ref);
QCOMPARE(bwdRect.toRect(), QRect(1398,1024,650,512));
QCOMPARE(bwdNastyRect.toRect(), QRect(1463,0,585,1232));
}
bool doPartialTests(const QString &prefix, KisImageSP image, KisLayerSP paintLayer,
KisLayerSP visibilityToggleLayer, KisTransformMaskSP mask)
{
TestUtil::ExternalImageChecker chk(prefix, "transform_mask_updates");
bool result = true;
QRect refRect = image->bounds();
int testIndex = 1;
QString testName;
for (int y = 0; y < refRect.height(); y += 512) {
for (int x = 0; x < refRect.width(); x += 512) {
QRect rc(x, y, 512, 512);
if (rc.right() > refRect.right()) {
rc.setRight(refRect.right());
if (rc.isEmpty()) continue;
}
if (rc.bottom() > refRect.bottom()) {
rc.setBottom(refRect.bottom());
if (rc.isEmpty()) continue;
}
paintLayer->setDirty(rc);
image->waitForDone();
testName = QString("tm_%1_partial_%2_%3").arg(testIndex++).arg(x).arg(y);
result &= chk.checkImage(image, testName);
}
}
// initial update of the mask to clear the unused portions of the projection
// (it updates only when we call set dirty on the mask itself, which happens
// in Krita right after the addition of the mask onto a layer)
mask->setDirty();
image->waitForDone();
testName = QString("tm_%1_initial_mask_visible_on").arg(testIndex++);
result &= chk.checkImage(image, testName);
// start layer visibility testing
paintLayer->setVisible(false);
paintLayer->setDirty();
image->waitForDone();
testName = QString("tm_%1_layer_visible_off").arg(testIndex++);
result &= chk.checkImage(image, testName);
paintLayer->setVisible(true);
paintLayer->setDirty();
image->waitForDone();
testName = QString("tm_%1_layer_visible_on").arg(testIndex++);
result &= chk.checkImage(image, testName);
if (paintLayer != visibilityToggleLayer) {
visibilityToggleLayer->setVisible(false);
visibilityToggleLayer->setDirty();
image->waitForDone();
testName = QString("tm_%1_extra_layer_visible_off").arg(testIndex++);
result &= chk.checkImage(image, testName);
visibilityToggleLayer->setVisible(true);
visibilityToggleLayer->setDirty();
image->waitForDone();
testName = QString("tm_%1_extra_layer_visible_on").arg(testIndex++);
result &= chk.checkImage(image, testName);
}
// toggle mask visibility
mask->setVisible(false);
mask->setDirty();
image->waitForDone();
testName = QString("tm_%1_mask_visible_off").arg(testIndex++);
result &= chk.checkImage(image, testName);
mask->setVisible(true);
mask->setDirty();
image->waitForDone();
testName = QString("tm_%1_mask_visible_on").arg(testIndex++);
result &= chk.checkImage(image, testName);
// entire bounds update
// no clearing, just don't hang up
paintLayer->setDirty(refRect);
image->waitForDone();
testName = QString("tm_%1_layer_dirty_bounds").arg(testIndex++);
result &= chk.checkImage(image, testName);
// no clearing, just don't hang up
mask->setDirty(refRect);
image->waitForDone();
testName = QString("tm_%1_mask_dirty_bounds").arg(testIndex++);
result &= chk.checkImage(image, testName);
if (paintLayer != visibilityToggleLayer) {
// no clearing, just don't hang up
visibilityToggleLayer->setDirty(refRect);
image->waitForDone();
testName = QString("tm_%1_extra_layer_dirty_bounds").arg(testIndex++);
result &= chk.checkImage(image, testName);
}
QRect fillRect;
// partial updates outside
fillRect = QRect(-100, 0.5 * refRect.height(), 50, 100);
paintLayer->paintDevice()->fill(fillRect, KoColor(Qt::red, image->colorSpace()));
paintLayer->setDirty(fillRect);
image->waitForDone();
testName = QString("tm_%1_layer_dirty_outside_%2_%3").arg(testIndex++).arg(fillRect.x()).arg(fillRect.y());
result &= chk.checkImage(image, testName);
fillRect = QRect(0.5 * refRect.width(), -100, 100, 50);
paintLayer->paintDevice()->fill(fillRect, KoColor(Qt::red, image->colorSpace()));
paintLayer->setDirty(fillRect);
image->waitForDone();
testName = QString("tm_%1_layer_dirty_outside_%2_%3").arg(testIndex++).arg(fillRect.x()).arg(fillRect.y());
result &= chk.checkImage(image, testName);
fillRect = QRect(refRect.width() + 50, 0.2 * refRect.height(), 50, 100);
paintLayer->paintDevice()->fill(fillRect, KoColor(Qt::red, image->colorSpace()));
paintLayer->setDirty(fillRect);
image->waitForDone();
testName = QString("tm_%1_layer_dirty_outside_%2_%3").arg(testIndex++).arg(fillRect.x()).arg(fillRect.y());
result &= chk.checkImage(image, testName);
// partial update inside
fillRect = QRect(0.5 * refRect.width() - 50, 0.5 * refRect.height() - 50, 100, 100);
paintLayer->paintDevice()->fill(fillRect, KoColor(Qt::red, image->colorSpace()));
paintLayer->setDirty(fillRect);
image->waitForDone();
testName = QString("tm_%1_layer_dirty_inside_%2_%3").arg(testIndex++).arg(fillRect.x()).arg(fillRect.y());
result &= chk.checkImage(image, testName);
// clear explicitly
image->projection()->clear();
mask->setDirty();
image->waitForDone();
testName = QString("tm_%1_mask_dirty_bounds").arg(testIndex++);
result &= chk.checkImage(image, testName);
KisDumbTransformMaskParams *params =
dynamic_cast<KisDumbTransformMaskParams*>(mask->transformParams().data());
QTransform t = params->testingGetTransform();
t *= QTransform::fromTranslate(400, 300);
params->testingSetTransform(t);
mask->setTransformParams(mask->transformParams());
mask->setDirty();
image->waitForDone();
testName = QString("tm_%1_mask_dirty_after_offset").arg(testIndex++);
result &= chk.checkImage(image, testName);
return result;
}
void KisTransformMaskTest::testMaskOnPaintLayer()
{
QImage refImage(TestUtil::fetchDataFileLazy("test_transform_quality.png"));
QRect refRect = refImage.rect();
TestUtil::MaskParent p(refRect);
p.layer->paintDevice()->convertFromQImage(refImage, 0);
KisPaintLayerSP player = new KisPaintLayer(p.image, "bg", OPACITY_OPAQUE_U8, p.image->colorSpace());
p.image->addNode(player, p.image->root(), KisNodeSP());
KisTransformMaskSP mask = new KisTransformMask();
p.image->addNode(mask, p.layer);
QTransform transform(-0.177454, -0.805953, -0.00213713,
-1.9295, -0.371835, -0.00290463,
3075.05, 2252.32, 7.62371);
mask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
QVERIFY(doPartialTests("pl", p.image, p.layer, p.layer, mask));
}
void KisTransformMaskTest::testMaskOnCloneLayer()
{
QImage refImage(TestUtil::fetchDataFileLazy("test_transform_quality.png"));
QRect refRect = refImage.rect();
TestUtil::MaskParent p(refRect);
p.layer->paintDevice()->convertFromQImage(refImage, 0);
KisPaintLayerSP player = new KisPaintLayer(p.image, "bg", OPACITY_OPAQUE_U8, p.image->colorSpace());
p.image->addNode(player, p.image->root(), KisNodeSP());
KisCloneLayerSP clone = new KisCloneLayer(p.layer, p.image, "clone", OPACITY_OPAQUE_U8);
p.image->addNode(clone, p.image->root());
KisTransformMaskSP mask = new KisTransformMask();
p.image->addNode(mask, clone);
QTransform transform(-0.177454, -0.805953, -0.00213713,
-1.9295, -0.371835, -0.00290463,
3075.05, 2252.32, 7.62371);
mask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
QVERIFY(doPartialTests("cl", p.image, p.layer, clone, mask));
}
void KisTransformMaskTest::testMaskOnCloneLayerWithOffset()
{
TestUtil::ExternalImageChecker chk("clone_offset_simple", "transform_mask_updates");
QRect refRect(0,0,512,512);
QRect fillRect(400,400,100,100);
TestUtil::MaskParent p(refRect);
p.layer->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
KisPaintLayerSP player = new KisPaintLayer(p.image, "bg", OPACITY_OPAQUE_U8, p.image->colorSpace());
p.image->addNode(player, p.image->root(), KisNodeSP());
KisCloneLayerSP clone = new KisCloneLayer(p.layer, p.image, "clone", OPACITY_OPAQUE_U8);
p.image->addNode(clone, p.image->root());
KisTransformMaskSP mask = new KisTransformMask();
p.image->addNode(mask, clone);
QTransform transform(1, 0, 0,
0, 1, 0,
0, -150, 1);
mask->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "0_initial");
clone->setX(-300);
clone->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "1_after_offset");
mask->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "2_after_offset_dirty_mask");
QTest::qWait(4000);
chk.checkImage(p.image, "3_delayed_regeneration");
KisPaintDeviceSP previewDevice = mask->buildPreviewDevice();
chk.checkDevice(previewDevice, p.image, "4_preview_device");
QVERIFY(chk.testPassed());
QVERIFY(doPartialTests("clone_offset_complex", p.image, p.layer, clone, mask));
}
#define CHECK_MASK1_TOGGLE
#define CHECK_MASK2_TOGGLE
#define CHECK_HIDE_ALL
#define CHECK_HIDE_ALL_AFTER_MOVE
void KisTransformMaskTest::testMultipleMasks()
{
TestUtil::ExternalImageChecker chk("multiple_masks", "transform_mask_updates");
QRect refRect(0,0,512,512);
QRect fillRect(400,400,100,100);
TestUtil::MaskParent p(refRect);
p.layer->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
KisPaintLayerSP player = new KisPaintLayer(p.image, "bg", OPACITY_OPAQUE_U8, p.image->colorSpace());
p.image->addNode(player, p.image->root(), KisNodeSP());
//KisCloneLayerSP clone = new KisCloneLayer(p.layer, p.image, "clone", OPACITY_OPAQUE_U8);
//p.image->addNode(clone, p.image->root());
KisTransformMaskSP mask1 = new KisTransformMask();
p.image->addNode(mask1, p.layer);
KisTransformMaskSP mask2 = new KisTransformMask();
p.image->addNode(mask2, p.layer);
mask1->setName("mask1");
mask2->setName("mask2");
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_layer_update");
QTransform transform;
transform = QTransform::fromTranslate(-150, 0);
mask1->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "01_mask1_moved_layer_update");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "01X_mask1_moved_layer_update");
transform = QTransform::fromTranslate(0, -150);
mask2->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "02_mask2_moved_layer_update");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "02X_mask2_moved_layer_update");
#ifdef CHECK_MASK1_TOGGLE
{
mask1->setVisible(false);
mask1->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "03_mask1_tg_off_refRect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "03X_mask1_tg_off_refRect");
mask1->setVisible(true);
mask1->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "04_mask1_tg_on_refRect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "04X_mask1_tg_on_refRect");
mask1->setVisible(false);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "05_mask1_tg_off_default_rect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "05X_mask1_tg_off_default_rect");
mask1->setVisible(true);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "06_mask1_tg_on_default_rect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "06X_mask1_tg_on_default_rect");
}
#endif /* CHECK_MASK1_TOGGLE */
#ifdef CHECK_MASK2_TOGGLE
{
mask2->setVisible(false);
mask2->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "07_mask2_tg_off_refRect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "07X_mask2_tg_off_refRect");
mask2->setVisible(true);
mask2->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "08_mask2_tg_on_refRect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "08X_mask2_tg_on_refRect");
mask2->setVisible(false);
mask2->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "09_mask2_tg_off_default_rect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "09X_mask2_tg_off_default_rect");
mask2->setVisible(true);
mask2->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "10_mask2_tg_on_default_rect");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "10X_mask2_tg_on_default_rect");
}
#endif /* CHECK_MASK2_TOGGLE */
#ifdef CHECK_HIDE_ALL
{
mask1->setVisible(false);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "11.1_hide_both_update_default_mask1");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "11.1X_hide_both_update_default_mask1");
mask2->setVisible(false);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "11.2_hide_both_update_default_mask2");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "11.2X_hide_both_update_default_mask2");
mask1->setVisible(true);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "12_sh_mask1_on");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "12X_sh_mask1_on");
mask1->setVisible(false);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "13_sh_mask1_off");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "13X_sh_mask1_off");
mask2->setVisible(true);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "14_sh_mask2_on");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "14X_sh_mask2_on");
mask2->setVisible(false);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "15_sh_mask2_off");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "15X_sh_mask2_off");
}
#endif /* CHECK_HIDE_ALL */
#ifdef CHECK_HIDE_ALL_AFTER_MOVE
{
transform = QTransform::fromTranslate(50, -150);
mask2->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "20_moved_mask2");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "20X_moved_mask2");
}
{
mask1->setVisible(false);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "21.1_hide_both_update_default_mask1");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "21.1X_hide_both_update_default_mask1");
mask2->setVisible(false);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "21.2_hide_both_update_default_mask2");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "21.2X_hide_both_update_default_mask2");
mask1->setVisible(true);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "22_sh_mask1_on");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "22X_sh_mask1_on");
mask1->setVisible(false);
mask1->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "23_sh_mask1_off");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "23X_sh_mask1_off");
mask2->setVisible(true);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "24_sh_mask2_on");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "24X_sh_mask2_on");
mask2->setVisible(false);
mask2->setDirty();
p.image->waitForDone();
chk.checkImage(p.image, "25_sh_mask2_off");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "25X_sh_mask2_off");
}
#endif /* CHECK_HIDE_ALL_AFTER_MOVE */
QVERIFY(chk.testPassed());
}
void KisTransformMaskTest::testMaskWithOffset()
{
TestUtil::ExternalImageChecker chk("mask_with_offset", "transform_mask_updates");
QRect refRect(0,0,512,512);
QRect fillRect(400,400,100,100);
TestUtil::MaskParent p(refRect);
p.layer->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
KisPaintLayerSP player = new KisPaintLayer(p.image, "bg", OPACITY_OPAQUE_U8, p.image->colorSpace());
p.image->addNode(player, p.image->root(), KisNodeSP());
KisTransformMaskSP mask1 = new KisTransformMask();
p.image->addNode(mask1, p.layer);
mask1->setName("mask1");
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "00_initial_layer_update");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "00X_initial_layer_update");
QTransform transform;
transform = QTransform::fromTranslate(-150, 0);
mask1->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform)));
p.layer->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "01_mask1_moved_layer_update");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "01X_mask1_moved_layer_update");
mask1->setY(-150);
mask1->setDirty(refRect);
p.image->waitForDone();
chk.checkImage(p.image, "02_mask1_y_offset");
QTest::qWait(4000);
p.image->waitForDone();
chk.checkImage(p.image, "02X_mask1_y_offset");
QVERIFY(chk.testPassed());
}
void KisTransformMaskTest::testWeirdFullUpdates()
{
//TestUtil::ExternalImageChecker chk("mask_with_offset", "transform_mask_updates");
QRect imageRect(0,0,512,512);
QRect fillRect(10, 10, 236, 236);
TestUtil::MaskParent p(imageRect);
p.layer->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
KisPaintLayerSP player1 = new KisPaintLayer(p.image, "pl1", OPACITY_OPAQUE_U8, p.image->colorSpace());
player1->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
p.image->addNode(player1, p.image->root());
KisTransformMaskSP mask1 = new KisTransformMask();
mask1->setName("mask1");
QTransform transform1 =
QTransform::fromTranslate(256, 0);
mask1->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform1)));
p.image->addNode(mask1, player1);
KisPaintLayerSP player2 = new KisPaintLayer(p.image, "pl2", OPACITY_OPAQUE_U8, p.image->colorSpace());
player2->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
p.image->addNode(player2, p.image->root());
KisTransformMaskSP mask2 = new KisTransformMask();
mask2->setName("mask2");
QTransform transform2 =
QTransform::fromTranslate(0, 256);
mask2->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform2)));
p.image->addNode(mask2, player2);
KisPaintLayerSP player3 = new KisPaintLayer(p.image, "pl3", OPACITY_OPAQUE_U8, p.image->colorSpace());
player3->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
p.image->addNode(player3, p.image->root());
KisTransformMaskSP mask3 = new KisTransformMask();
mask3->setName("mask3");
QTransform transform3 =
QTransform::fromTranslate(256, 256);
mask3->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform3)));
p.image->addNode(mask3, player3);
//p.image->initialRefreshGraph();
p.image->refreshGraphAsync(0, QRect(0,0,256,256), QRect());
p.image->waitForDone();
QVERIFY(player1->projection()->extent().isEmpty());
QVERIFY(player1->projection()->exactBounds().isEmpty());
QVERIFY(player2->projection()->extent().isEmpty());
QVERIFY(player2->projection()->exactBounds().isEmpty());
QVERIFY(player3->projection()->extent().isEmpty());
QVERIFY(player3->projection()->exactBounds().isEmpty());
QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,236,236)));
p.image->refreshGraphAsync(0, QRect(0,256,256,256), QRect());
p.image->waitForDone();
QVERIFY(player1->projection()->extent().isEmpty());
QVERIFY(player1->projection()->exactBounds().isEmpty());
QVERIFY(!player2->projection()->extent().isEmpty());
QVERIFY(!player2->projection()->exactBounds().isEmpty());
QVERIFY(player3->projection()->extent().isEmpty());
QVERIFY(player3->projection()->exactBounds().isEmpty());
QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,236,492)));
p.image->refreshGraphAsync(0, QRect(256,0,256,256), QRect());
p.image->waitForDone();
QVERIFY(!player1->projection()->extent().isEmpty());
QVERIFY(!player1->projection()->exactBounds().isEmpty());
QVERIFY(!player2->projection()->extent().isEmpty());
QVERIFY(!player2->projection()->exactBounds().isEmpty());
QVERIFY(player3->projection()->extent().isEmpty());
QVERIFY(player3->projection()->exactBounds().isEmpty());
QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,492,492)));
QVERIFY((p.image->projection()->region() & QRect(256,256,256,256)).isEmpty());
p.image->refreshGraphAsync(0, QRect(256,256,256,256), QRect());
p.image->waitForDone();
QVERIFY(!player1->projection()->extent().isEmpty());
QVERIFY(!player1->projection()->exactBounds().isEmpty());
QVERIFY(!player2->projection()->extent().isEmpty());
QVERIFY(!player2->projection()->exactBounds().isEmpty());
QVERIFY(!player3->projection()->extent().isEmpty());
QVERIFY(!player3->projection()->exactBounds().isEmpty());
QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,492,492)));
QVERIFY(!(p.image->projection()->region() & QRect(256,256,256,256)).isEmpty());
p.image->waitForDone();
KIS_DUMP_DEVICE_2(p.image->projection(), imageRect, "image_proj", "dd");
}
void KisTransformMaskTest::testTransformHiddenPartsOfTheGroup()
{
//TestUtil::ExternalImageChecker chk("mask_with_offset", "transform_mask_updates");
QRect imageRect(0,0,512,512);
QRect fillRect(10, 10, 236, 236);
QRect outsideFillRect = fillRect.translated(0, -1.5 * 256);
TestUtil::MaskParent p(imageRect);
//p.layer->paintDevice()->fill(fillRect, KoColor(Qt::green, p.layer->colorSpace()));
p.image->initialRefreshGraph();
KisGroupLayerSP glayer = new KisGroupLayer(p.image, "gl", OPACITY_OPAQUE_U8);
p.image->addNode(glayer, p.image->root());
KisPaintLayerSP player1 = new KisPaintLayer(p.image, "pl1", OPACITY_OPAQUE_U8, p.image->colorSpace());
player1->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
player1->paintDevice()->fill(outsideFillRect, KoColor(Qt::blue, p.layer->colorSpace()));
p.image->addNode(player1, glayer);
player1->setDirty();
p.image->waitForDone();
QCOMPARE(p.image->projection()->exactBounds(), fillRect);
//KIS_DUMP_DEVICE_2(p.image->projection(), imageRect, "image_proj_initial", "dd");
KisTransformMaskSP mask1 = new KisTransformMask();
mask1->setName("mask1");
QTransform transform1 =
QTransform::fromTranslate(0, 1.5 * 256);
mask1->setTransformParams(KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(transform1)));
p.image->addNode(mask1, player1);
mask1->setDirty();
p.image->waitForDone();
/**
* Transform mask i sexpected to crop the externals of the layer by 50%
* far behind the layer border! Take care!
*/
QCOMPARE(p.image->projection()->exactBounds(), QRect(10, 128, 236, 384));
//KIS_DUMP_DEVICE_2(p.image->projection(), imageRect, "image_proj_mask", "dd");
}
QTEST_KDEMAIN(KisTransformMaskTest, GUI)
diff --git a/krita/image/tests/kis_transform_worker_test.cpp b/krita/image/tests/kis_transform_worker_test.cpp
index 5a41efc48e2..bbcb341d2b5 100644
--- a/krita/image/tests/kis_transform_worker_test.cpp
+++ b/krita/image/tests/kis_transform_worker_test.cpp
@@ -1,997 +1,997 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transform_worker_test.h"
#include <qtest_kde.h>
#include <KoProgressUpdater.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <QTransform>
#include <QVector>
#include "kis_types.h"
#include "kis_image.h"
#include "kis_filter_strategy.h"
#include "kis_paint_device.h"
#include "kis_transform_worker.h"
#include "testutil.h"
#include "kis_transaction.h"
#include "kis_random_accessor_ng.h"
void KisTransformWorkerTest::testCreation()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisTransformWorker tw(dev, 1.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.5,
0, 0, updater, filter);
}
void testMirror(const QRect &imageRect, const QRect &mirrorRect, Qt::Orientation orientation)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
qreal axis = QRectF(mirrorRect).center().x();
KisRandomAccessorSP it = dev->createRandomAccessorNG(imageRect.x(), imageRect.y());
int i = 0;
for (int y = imageRect.y(); y < imageRect.y() + imageRect.height(); y++) {
for (int x = imageRect.x(); x < imageRect.x() + imageRect.width(); x++) {
it->moveTo(x, y);
*reinterpret_cast<quint32*>(it->rawData()) = 0xFFFFFFFF - i++;
}
}
QCOMPARE(dev->exactBounds(), imageRect);
QImage srcImage = dev->convertToQImage(0, mirrorRect.x(), mirrorRect.y(), mirrorRect.width(), mirrorRect.height());
QImage mirroredImage = srcImage.mirrored(orientation == Qt::Horizontal, orientation == Qt::Vertical);
QImage result;
//srcImage.save("input.png");
//mirroredImage.save("mirror_expected.png");
QBENCHMARK_ONCE {
KisTransformWorker::mirror(dev, axis, orientation);
}
result = dev->convertToQImage(0, mirrorRect.x(), mirrorRect.y(), mirrorRect.width(), mirrorRect.height());
QCOMPARE(result, mirroredImage);
//result.save("mirror1.png");
KisTransformWorker::mirror(dev, axis, orientation);
result = dev->convertToQImage(0, mirrorRect.x(), mirrorRect.y(), mirrorRect.width(), mirrorRect.height());
QCOMPARE(result, srcImage);
//result.save("mirror2.png");
KisTransformWorker::mirror(dev, axis, orientation);
result = dev->convertToQImage(0, mirrorRect.x(), mirrorRect.y(), mirrorRect.width(), mirrorRect.height());
QCOMPARE(result, mirroredImage);
//result.save("mirror3.png");
}
void KisTransformWorkerTest::testMirrorX_Even()
{
testMirror(QRect(10,10,30,30), QRect(1,1,70,70), Qt::Horizontal);
}
void KisTransformWorkerTest::testMirrorX_Odd()
{
testMirror(QRect(10,10,30,30), QRect(1,1,71,71), Qt::Horizontal);
}
void KisTransformWorkerTest::testMirrorY_Even()
{
testMirror(QRect(10,10,30,30), QRect(1,1,70,70), Qt::Vertical);
}
void KisTransformWorkerTest::testMirrorY_Odd()
{
testMirror(QRect(10,10,30,30), QRect(1,1,71,71), Qt::Vertical);
}
void KisTransformWorkerTest::benchmarkMirrorX()
{
testMirror(QRect(10,10,4000,4000), QRect(1,1,7000,7000), Qt::Horizontal);
}
void KisTransformWorkerTest::benchmarkMirrorY()
{
testMirror(QRect(10,10,4000,4000), QRect(1,1,7000,7000), Qt::Vertical);
}
void KisTransformWorkerTest::testOffset()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QString imageName("mirror_source.png");
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + imageName);
QPoint bottomRight(image.width(), image.height());
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
QVector<QPoint> offsetPoints;
offsetPoints.append(QPoint(image.width() / 2, image.height() / 2)); // offset to 1/2 of image
offsetPoints.append(QPoint(image.width() / 4, image.height() / 4)); // offset to 1/4 of image
offsetPoints.append(QPoint(image.width() - image.width() / 4, image.height() - image.height() / 4)); // offset to 3/4 of image
offsetPoints.append(QPoint(image.width() / 4, 0)); // offset with y == 0
offsetPoints.append(QPoint(0, image.height() / 4)); // offset with x == 0
QPoint errpoint;
QPoint backOffsetPoint;
QImage result;
int test = 0;
QPoint origin(0,0);
foreach (QPoint offsetPoint, offsetPoints)
{
dev2->convertFromQImage(image, 0);
KisTransformWorker::offset(dev2, offsetPoint, QRect(origin, image.size()) );
backOffsetPoint = bottomRight - offsetPoint;
KisTransformWorker::offset(dev2, backOffsetPoint , QRect(origin, image.size()) );
result = dev2->convertToQImage(0, 0, 0, image.width(), image.height());
if (!TestUtil::compareQImages(errpoint, image, result))
{
// They are the same, but should be mirrored
image.save(QString("offset_test_%1_source.png").arg(test));
result.save(QString("offset_test_%1_result.png").arg(test));
QFAIL(QString("Failed to offset the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
}
void KisTransformWorkerTest::testMirrorTransactionX()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->convertFromQImage(image, 0);
KisTransaction t(kundo2_noi18n("mirror"), dev2);
KisTransformWorker::mirrorX(dev2);
t.end();
QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height());
image = image.mirrored(true, false);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
// They are the same, but should be mirrored
image.save("mirror_test_3_source.png");
result.save("mirror_test_3_result.png");
QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testMirrorTransactionY()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->convertFromQImage(image, 0);
KisTransaction t(kundo2_noi18n("mirror"), dev2);
KisTransformWorker::mirrorY(dev2);
t.end();
QImage result = dev2->convertToQImage(0, 0, 0, image.width(), image.height());
image = image.mirrored(false, true);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
// They are the same, but should be mirrored
image.save("mirror_test_4_source.png");
result.save("mirror_test_4_result.png");
QFAIL(QString("Failed to mirror the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testScaleUp()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 2.4, 2.4,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QCOMPARE(rc.width(), qCeil(image.width() * 2.4));
QCOMPARE(rc.height(), qCeil(image.height() * 2.4));
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "test_scaleup_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("test_scaleup_source.png");
result.save("test_scaleup_result.png");
QFAIL(QString("Failed to scale the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testXScaleUp()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 2.0, 1.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QVERIFY(rc.width() == image.width() * 2);
QVERIFY(rc.height() == image.height());
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaleupx_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("test_x_scaleup_source.png");
result.save("test_x_scaleup_result.png");
QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testYScaleUp()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 2.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QCOMPARE(rc.width(), image.width());
QCOMPARE(rc.height(), image.height() * 2);
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaleupy_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("test_y_scaleup_source.png");
result.save("test_y_scaleup_result.png");
QFAIL(QString("Failed to scale up the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testIdentity()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 1.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QVERIFY(rc.width() ==image.width());
QVERIFY(rc.height() == image.height());
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("test_identity_source.png");
result.save("test_identity_result.png");
QFAIL(QString("Failed to apply identity transformation to image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testScaleDown()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 0.123, 0.123,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QCOMPARE(rc.width(), qCeil(image.width() * 0.123));
QCOMPARE(rc.height(), qCeil(image.height() * 0.123));
// KisTransaction t2("test", dev);
// KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y());
// for(int x = rc.x(); x < rc.width(); ++x) {
// for(int y = rc.y(); y < rc.height(); ++y) {
// ac->moveTo(x, y);
// cs->setOpacity(ac->rawData(), 0.5, 1);
// }
// }
// t2.end();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "test_scaledown_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("test_scaledown_source.png");
result.save("test_scaledown_result.png");
QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testXScaleDown()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 0.123, 1.0,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QCOMPARE(rc.width(), qCeil(image.width() * 0.123));
QCOMPARE(rc.height(), image.height());
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaledownx_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("scaledownx_source.png");
result.save("scaledownx_result.png");
QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testYScaleDown()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 0.123,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QCOMPARE(rc.width(), image.width());
QCOMPARE(rc.height(), qCeil(image.height() * 0.123));
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "scaledowny_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("scaledowny_source.png");
result.save("scaledowny_result.png");
QFAIL(QString("Failed to scale down the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testXShear()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 1.0,
1.0, 0.0,
300., 200.,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QVERIFY(rc.width() == 959);
QVERIFY(rc.height() == image.height());
// KisTransaction t2("test", dev);
// KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y());
// for(int x = rc.x(); x < rc.width(); ++x) {
// for(int y = rc.y(); y < rc.height(); ++y) {
// ac->moveTo(x, y);
// cs->setOpacity(ac->rawData(), 0.5, 1);
// }
// }
// t2.end();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "shearx_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("shearx_source.png");
result.save("shearx_result.png");
QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testYShear()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBoxFilterStrategy();
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 1.0,
0.0, 1.0,
300., 200.,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QVERIFY(rc.width() == image.width());
QVERIFY(rc.height() == 959);
// KisTransaction t2("test", dev);
// KisRandomAccessorSP ac = dev->createRandomAccessorNG(rc.x(), rc.y());
// for(int x = rc.x(); x < rc.width(); ++x) {
// for(int y = rc.y(); y < rc.height(); ++y) {
// ac->moveTo(x, y);
// cs->setOpacity(ac->rawData(), 0.5, 1);
// }
// }
// t2.end();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
image.load(QString(FILES_DATA_DIR) + QDir::separator() + "sheary_result.png");
if (!TestUtil::compareQImages(errpoint, image, result)) {
image.save("sheary_source.png");
result.save("sheary_result.png");
QFAIL(QString("Failed to shear the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
bool fuzzyCompareRects(const QRectF rc1, const QRectF rc2, qreal accuracy)
{
bool result =
qAbs(rc1.x() - rc2.x()) < accuracy &&
qAbs(rc1.y() - rc2.y()) < accuracy &&
qAbs(rc1.width() - rc2.width()) < 2 * accuracy &&
qAbs(rc1.height() - rc2.height()) < 2 * accuracy;
if(!result) {
- qDebug() << "Failed to fuzzy compare rects";
- qDebug() << "\t" << ppVar(accuracy);
- qDebug() << "\t" << "actual " << rc1;
- qDebug() << "\t" << "expected" << rc2;
- qDebug() << "+---------------------------+";
+ dbgKrita << "Failed to fuzzy compare rects";
+ dbgKrita << "\t" << ppVar(accuracy);
+ dbgKrita << "\t" << "actual " << rc1;
+ dbgKrita << "\t" << "expected" << rc2;
+ dbgKrita << "+---------------------------+";
}
return result;
}
void KisTransformWorkerTest::testMatrices()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
KisFilterStrategy *filter = new KisBoxFilterStrategy();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QRect fillRect(0,0,300,200);
KoColor fillColor(Qt::white, cs);
dev->fill(fillRect, fillColor);
qreal scaleX = 1.5, scaleY = 1.5;
qreal shearX = 1, shearY = 1.33;
qreal shearOrigX = 150, shearOrigY = 100;
qreal angle = M_PI/6;
qreal transX = 77, transY = 33;
KisTransaction t(dev);
KisTransformWorker tw(dev, scaleX, scaleY,
shearX, shearY,
shearOrigX, shearOrigY,
angle,
transX, transY,
updater, filter);
tw.run();
t.end();
QPolygonF referencePolygon =
tw.transform().map(QPolygonF(QRectF(fillRect)));
QVERIFY(fuzzyCompareRects(dev->exactBounds(),
referencePolygon.boundingRect(), 3));
}
void testRotationImpl(qreal angle, QString filePrefix, bool useUniformColor = false, const QString &filterId = "Box")
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QImage image;
if (!useUniformColor) {
image = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "mirror_source.png");
dev->convertFromQImage(image, 0);
} else {
dev->fill(QRect(120, 130, 374, 217), KoColor(QColor(150, 180, 230), cs));
}
KisFilterStrategy * filter = KisFilterStrategyRegistry::instance()->value(filterId);
Q_ASSERT(filter);
KisTransaction t(dev);
KisTransformWorker tw(dev, 1.0, 1.0,
0.0, 0.0,
0.0, 0.0,
angle,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
QPoint errpoint;
QString resFileName = QString("%1_result.png").arg(filePrefix);
QString refFileName = QString("%1_expected.png").arg(filePrefix);
image = QImage();
image.load(QString(FILES_DATA_DIR) + QDir::separator() + resFileName);
if (!TestUtil::compareQImages(errpoint, image, result)) {
- qDebug() << filePrefix;
+ dbgKrita << filePrefix;
image.save(refFileName);
result.save(resFileName);
QFAIL(QString("Failed to rotate the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::testRotation()
{
testRotationImpl(M_PI/6, "rotation_30");
testRotationImpl(M_PI/3, "rotation_60");
testRotationImpl(M_PI/2, "rotation_90");
testRotationImpl(2*M_PI/3, "rotation_120");
testRotationImpl(7*M_PI/6, "rotation_210");
testRotationImpl(5*M_PI/3, "rotation_300");
testRotationImpl(M_PI/6, "rotation_30_uniform_blin", true, "Bilinear");
testRotationImpl(M_PI/6, "rotation_30_uniform_bcub", true, "Bicubic");
testRotationImpl(M_PI/6, "rotation_30_uniform_lanc", true, "Lanczos3");
}
void KisTransformWorkerTest::testRotationSpecialCases()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
KisFilterStrategy *filter = new KisBoxFilterStrategy();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
QRect fillRect(0,0,600,300);
KoColor fillColor(Qt::white, cs);
dev->fill(fillRect, fillColor);
qreal scaleX = 0.5, scaleY = 0.5;
qreal shearX = 0, shearY = 0;
qreal shearOrigX = 0, shearOrigY = 0;
qreal angle = M_PI;
qreal transX = 300, transY = 150;
KisTransaction t(dev);
KisTransformWorker tw(dev, scaleX, scaleY,
shearX, shearY,
shearOrigX, shearOrigY,
angle,
transX, transY,
updater, filter);
tw.run();
t.end();
QCOMPARE(dev->exactBounds(), QRect(0,0,300,150));
}
void KisTransformWorkerTest::testScaleUp5times()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
QImage image(QSize(2000,2000), QImage::Format_ARGB32_Premultiplied);
image.fill(QColor(Qt::green).rgba());
int checkSize = 20;
QImage tile(checkSize * 2, checkSize * 2, QImage::Format_ARGB32_Premultiplied);
QPainter pt(&tile);
pt.fillRect(tile.rect(), Qt::green);
pt.fillRect(0, 0, checkSize, checkSize, Qt::white);
pt.fillRect(checkSize, checkSize, checkSize, checkSize, Qt::white);
pt.end();
pt.begin(&image);
pt.setBrush(QBrush(tile));
pt.drawRect(image.rect());
pt.end();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisFilterStrategy * filter = new KisBicubicFilterStrategy();
KisTransaction t(dev);
qreal SCALE = 5.0;
KisTransformWorker tw(dev, SCALE, SCALE,
0.0, 0.0,
0.0, 0.0,
0.0,
0, 0, updater, filter);
tw.run();
t.end();
QRect rc = dev->exactBounds();
#if 0
// here you can check the input and result images
image.save("test_scale_2000_2000_input.bmp");
QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
result.save("test_scale_2000_2000_" + QString::number(SCALE) + "_result.bmp");
#endif
QCOMPARE(rc.width(), qCeil(image.width() * SCALE));
QCOMPARE(rc.height(), qCeil(image.height() * SCALE));
}
void KisTransformWorkerTest::rotate90Left()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png");
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->convertFromQImage(image, 0);
QRect boundRect = dev2->exactBounds();
KisTransaction t(kundo2_noi18n("rotate left 90"), dev2);
QRect rc = KisTransformWorker::rotateLeft90(dev2, boundRect, 0, 0);
t.end();
QImage result = dev2->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height());
QTransform tf;
QImage rotatedimage = image.transformed(tf.rotate(270));
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) {
// They are the same, but should be mirrored
image.save("rotate_90_left_test_1_source.png");
rotatedimage.save("rotate_90_left_test_1_rotated_source.png");
result.save("rotate_90_left_test_1_result.png");
QFAIL(QString("Failed to rotate 90 left the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::rotate90Right()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png");
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->convertFromQImage(image, 0);
QRect boundRect = dev2->exactBounds();
KisTransaction t(kundo2_noi18n("rotate right 90"), dev2);
QRect rc = KisTransformWorker::rotateRight90(dev2, boundRect, 0, 0);
t.end();
QTransform tf;
QImage rotatedimage = image.transformed(tf.rotate(90));
QImage result = dev2->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height());
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) {
// They are the same, but should be mirrored
image.save("rotate_90_right_test_1_source.png");
rotatedimage.save("rotate_90_right_test_1_rotated_source.png");
result.save("rotate_90_right_1_result.png");
QFAIL(QString("Failed to rotate 90 right the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void KisTransformWorkerTest::rotate180()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "transform_rotate_test.png");
KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
dev2->convertFromQImage(image, 0);
QRect boundRect = dev2->exactBounds();
KisTransaction t(kundo2_noi18n("rotate 180"), dev2);
QRect rc = KisTransformWorker::rotate180(dev2, boundRect, 0, 0);
t.end();
QImage result = dev2->convertToQImage(0, rc.x(), rc.y(), image.width(), image.height());
QTransform tf;
QImage rotatedimage = image.transformed(tf.rotate(180));
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint, rotatedimage, result)) {
// They are the same, but should be mirrored
image.save("rotate_180_1_source.png");
rotatedimage.save("rotate_180_1_rotated_source.png");
result.save("rotate_180_1_result.png");
QFAIL(QString("Failed to rotate 180 the image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
void generateTestImage(QString inputFileName, qreal scale, qreal rotation, qreal xshear, KisFilterStrategy *filter, bool saveImage = true)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + inputFileName);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
KisTransformWorker tw(dev, scale, scale,
xshear, 0.0,
0.0, 0.0,
rotation,
0, 0,
updater, filter);
tw.run();
if (saveImage) {
QStringList tmp = inputFileName.split('.');
QString filename =
QString("transform_%1_%2_%3_%4_%5_new.png")
.arg(tmp[0]).arg(scale).arg(rotation).arg(xshear).arg(filter->name());
- qDebug() << filename;
+ dbgKrita << filename;
dev->convertToQImage(0).save(filename);
}
}
void KisTransformWorkerTest::benchmarkScale()
{
QBENCHMARK {
generateTestImage("hakonepa.png", 1.379,0.0,0.0,new KisBicubicFilterStrategy(), false);
}
}
void KisTransformWorkerTest::benchmarkRotate()
{
QBENCHMARK {
generateTestImage("hakonepa.png", 1.0,M_PI/6.0,0.0,new KisBicubicFilterStrategy(), false);
}
}
void KisTransformWorkerTest::benchmarkRotate1Q()
{
QBENCHMARK {
generateTestImage("hakonepa.png", 1.0,2 * M_PI/3.0,0.0,new KisBicubicFilterStrategy(), false);
}
}
void KisTransformWorkerTest::benchmarkShear()
{
QBENCHMARK {
generateTestImage("hakonepa.png", 1.0,0.0,0.479,new KisBicubicFilterStrategy(), false);
}
}
void KisTransformWorkerTest::benchmarkScaleRotateShear()
{
QBENCHMARK {
generateTestImage("hakonepa.png", 1.379,M_PI/6.0,0.479,new KisBicubicFilterStrategy(), false);
}
}
void KisTransformWorkerTest::generateTestImages()
{
QList<KisFilterStrategy*> filters;
filters << new KisBoxFilterStrategy();
filters << new KisHermiteFilterStrategy();
filters << new KisBicubicFilterStrategy();
filters << new KisBilinearFilterStrategy();
filters << new KisBellFilterStrategy();
filters << new KisBSplineFilterStrategy();
filters << new KisLanczos3FilterStrategy();
filters << new KisMitchellFilterStrategy();
QStringList names;
names << "hakonepa.png";
//names << "star-chart-bars-full-600dpi.png";
foreach(const QString &name, names) {
foreach(KisFilterStrategy *filter, filters) {
generateTestImage(name, 0.5,0.0,0.0,filter);
generateTestImage(name, 0.734,0.0,0.0,filter);
generateTestImage(name, 1.387,0.0,0.0,filter);
generateTestImage(name, 2.0,0.0,0.0,filter);
generateTestImage(name, 3.789,0.0,0.0,filter);
generateTestImage(name, 1.0,M_PI/6,0.0,filter);
generateTestImage(name, 1.0,0.0,0.5,filter);
}
}
}
#include "kis_perspectivetransform_worker.h"
void KisTransformWorkerTest::testPartialProcessing()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
KisTransaction t(dev);
QTransform transform = QTransform::fromScale(2.0, 1.1);
transform.shear(1.1, 0);
transform.rotateRadians(M_PI / 18);
KisPerspectiveTransformWorker tw(0, transform, updater);
tw.runPartialDst(dev, dev, QRect(1200, 1200, 150, 150));
tw.runPartialDst(dev, dev, QRect(1350, 1200, 150, 150));
tw.runPartialDst(dev, dev, QRect(1200, 1350, 150, 150));
tw.runPartialDst(dev, dev, QRect(1350, 1350, 150, 150));
t.end();
QImage result = dev->convertToQImage(0);
TestUtil::checkQImage(result, "transform_test", "partial", "single");
}
QTEST_KDEMAIN(KisTransformWorkerTest, GUI)
diff --git a/krita/image/tests/kis_transparency_mask_test.cpp b/krita/image/tests/kis_transparency_mask_test.cpp
index e45e5b39cf2..3a199e55cad 100644
--- a/krita/image/tests/kis_transparency_mask_test.cpp
+++ b/krita/image/tests/kis_transparency_mask_test.cpp
@@ -1,214 +1,214 @@
/*
* Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_transparency_mask_test.h"
#include <qtest_kde.h>
#include "kis_transparency_mask.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_fill_painter.h"
#include "testutil.h"
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#define IMAGE_WIDTH 1000
#define IMAGE_HEIGHT 1000
KisPaintDeviceSP createDevice()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisFillPainter gc(dev);
KoColor c(Qt::red, dev->colorSpace());
gc.fillRect(0, 0, 100, 100, c);
c = KoColor(Qt::blue, dev->colorSpace());
gc.fillRect(100, 0, 100, 100, c);
gc.end();
return dev;
}
void KisTransparencyMaskTest::testCreation()
{
KisTransparencyMask test;
}
#define initImage(image, layer, device, mask) do { \
image = new KisImage(0, IMAGE_WIDTH, IMAGE_HEIGHT, 0, "tests"); \
device = createDevice(); \
layer = new KisPaintLayer(image, "paint1", 100, device); \
mask = new KisTransparencyMask(); \
image->addNode(layer); \
image->addNode(mask, layer); \
} while(0)
void KisTransparencyMaskTest::testApply()
{
QPoint errpoint;
KisImageSP image;
KisPaintLayerSP layer;
KisPaintDeviceSP dev;
KisTransparencyMaskSP mask;
QRect applyRect(0, 0, 200, 100);
// Everything is selected
initImage(image, layer, dev, mask);
mask->initSelection(layer);
mask->apply(dev, applyRect, applyRect, KisNode::N_FILTHY);
QImage qimage = dev->convertToQImage(0, 0, 0, 200, 100);
if (!TestUtil::compareQImages(errpoint,
QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_2.png"),
qimage)) {
QFAIL(QString("Failed to mask out image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
// Invert the mask, so that nothing will be selected, then select a rect
initImage(image, layer, dev, mask);
mask->initSelection(layer);
mask->selection()->pixelSelection()->invert();
mask->apply(dev, applyRect, applyRect, KisNode::N_FILTHY);
qimage = dev->convertToQImage(0, 0, 0, 200, 100);
if (!TestUtil::compareQImages(errpoint,
QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_1.png"),
qimage)) {
QFAIL(QString("Failed to mask in image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
initImage(image, layer, dev, mask);
mask->initSelection(layer);
mask->selection()->pixelSelection()->invert();
mask->select(QRect(50, 0, 100, 100));
mask->apply(dev, applyRect, applyRect, KisNode::N_FILTHY);
qimage = dev->convertToQImage(0, 0, 0, 200, 100);
if (!TestUtil::compareQImages(errpoint,
QImage(QString(FILES_DATA_DIR) + QDir::separator() + "transparency_mask_test_3.png"),
qimage)) {
QFAIL(QString("Failed to apply partial mask, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
}
#include "kis_full_refresh_walker.h"
#include "kis_async_merger.h"
void KisTransparencyMaskTest::testMoveParentLayer()
{
KisImageSP image;
KisPaintLayerSP layer;
KisPaintDeviceSP dev;
KisTransparencyMaskSP mask;
initImage(image, layer, dev, mask);
mask->initSelection(layer);
mask->selection()->pixelSelection()->invert();
mask->select(QRect(50, 50, 100, 100));
KisFullRefreshWalker walker(image->bounds());
KisAsyncMerger merger;
walker.collectRects(layer, image->bounds());
merger.startMerge(walker);
// image->projection()->convertToQImage(0, 0,0,300,300).save("proj_before.png");
QRect initialRect(0,0,200,100);
QCOMPARE(layer->exactBounds(), initialRect);
QCOMPARE(image->projection()->exactBounds(), QRect(50,50,100,50));
layer->setX(100);
layer->setY(100);
- qDebug() << "Sel. rect before:" << mask->selection()->selectedExactRect();
+ dbgKrita << "Sel. rect before:" << mask->selection()->selectedExactRect();
mask->setX(100);
mask->setY(100);
- qDebug() << "Sel. rect after:" << mask->selection()->selectedExactRect();
+ dbgKrita << "Sel. rect after:" << mask->selection()->selectedExactRect();
QRect finalRect(100,100,200,100);
QCOMPARE(layer->exactBounds(), finalRect);
walker.collectRects(layer, initialRect | finalRect);
merger.startMerge(walker);
// image->projection()->convertToQImage(0, 0,0,300,300).save("proj_after.png");
QCOMPARE(image->projection()->exactBounds(), QRect(150,150,100,50));
}
void KisTransparencyMaskTest::testMoveMaskItself()
{
KisImageSP image;
KisPaintLayerSP layer;
KisPaintDeviceSP dev;
KisTransparencyMaskSP mask;
initImage(image, layer, dev, mask);
mask->initSelection(layer);
mask->selection()->pixelSelection()->invert();
mask->select(QRect(50, 50, 100, 100));
KisFullRefreshWalker walker(image->bounds());
KisAsyncMerger merger;
walker.collectRects(layer, image->bounds());
merger.startMerge(walker);
// image->projection()->convertToQImage(0, 0,0,300,300).save("proj_before.png");
QRect initialRect(0,0,200,100);
QCOMPARE(layer->exactBounds(), initialRect);
QCOMPARE(image->projection()->exactBounds(), QRect(50,50,100,50));
//layer->setX(100);
//layer->setY(100);
- qDebug() << "Sel. rect before:" << mask->selection()->selectedExactRect();
+ dbgKrita << "Sel. rect before:" << mask->selection()->selectedExactRect();
mask->setX(50);
mask->setY(25);
- qDebug() << "Sel. rect after:" << mask->selection()->selectedExactRect();
+ dbgKrita << "Sel. rect after:" << mask->selection()->selectedExactRect();
QCOMPARE(mask->selection()->selectedExactRect(), QRect(100, 75, 100, 100));
QCOMPARE(layer->paintDevice()->exactBounds(), initialRect);
QCOMPARE(layer->projection()->exactBounds(), QRect(50, 50, 100, 50));
- qDebug() << "";
+ dbgKrita << "";
QRect updateRect(0,0,300,300);
walker.collectRects(mask, updateRect);
merger.startMerge(walker);
// image->projection()->convertToQImage(0, 0,0,300,300).save("proj_after.png");
QCOMPARE(layer->paintDevice()->exactBounds(), initialRect);
QCOMPARE(layer->projection()->exactBounds(), QRect(100, 75, 100, 25));
}
QTEST_KDEMAIN(KisTransparencyMaskTest, GUI)
diff --git a/krita/image/tests/kis_update_scheduler_test.cpp b/krita/image/tests/kis_update_scheduler_test.cpp
index 4a4f16ef152..4f56228440f 100644
--- a/krita/image/tests/kis_update_scheduler_test.cpp
+++ b/krita/image/tests/kis_update_scheduler_test.cpp
@@ -1,415 +1,415 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_update_scheduler_test.h"
#include <qtest_kde.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_group_layer.h"
#include "kis_paint_layer.h"
#include "kis_adjustment_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "scheduler_utils.h"
#include "kis_update_scheduler.h"
#include "kis_updater_context.h"
#include "kis_update_job_item.h"
#include "kis_simple_update_queue.h"
#include "../../sdk/tests/testutil.h"
KisImageSP KisUpdateSchedulerTest::buildTestingImage()
{
QImage sourceImage1(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");
QImage sourceImage2(QString(FILES_DATA_DIR) + QDir::separator() + "inverted_hakonepa.png");
QRect imageRect = QRect(QPoint(0,0), sourceImage1.size());
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration = filter->defaultConfiguration(0);
Q_ASSERT(configuration);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8 / 3);
KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
paintLayer1->paintDevice()->convertFromQImage(sourceImage1, 0, 0, 0);
paintLayer2->paintDevice()->convertFromQImage(sourceImage2, 0, 0, 0);
image->lock();
image->addNode(paintLayer1);
image->addNode(paintLayer2);
image->addNode(blur1);
image->unlock();
return image;
}
void KisUpdateSchedulerTest::testMerge()
{
KisImageSP image = buildTestingImage();
QRect imageRect = image->bounds();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QCOMPARE(paintLayer1->name(), QString("paint1"));
KisUpdateScheduler scheduler(image.data());
/**
* Test synchronous Full Refresh
*/
scheduler.fullRefresh(rootLayer, image->bounds(), image->bounds());
QCOMPARE(rootLayer->exactBounds(), image->bounds());
QImage resultFRProjection = rootLayer->projection()->convertToQImage(0);
resultFRProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_fr_merge_result.png");
/**
* Test incremental updates
*/
rootLayer->projection()->clear();
const qint32 num = 4;
qint32 width = imageRect.width() / num;
qint32 lastWidth = imageRect.width() - width;
QVector<QRect> dirtyRects(num);
for(qint32 i = 0; i < num-1; i++) {
dirtyRects[i] = QRect(width*i, 0, width, imageRect.height());
}
dirtyRects[num-1] = QRect(width*(num-1), 0, lastWidth, imageRect.height());
for(qint32 i = 0; i < num; i+=2) {
scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
}
for(qint32 i = 1; i < num; i+=2) {
scheduler.updateProjection(paintLayer1, dirtyRects[i], image->bounds());
}
scheduler.waitForDone();
QCOMPARE(rootLayer->exactBounds(), image->bounds());
QImage resultDirtyProjection = rootLayer->projection()->convertToQImage(0);
resultDirtyProjection.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + "scheduler_dp_merge_result.png");
QPoint pt;
QVERIFY(TestUtil::compareQImages(pt, resultFRProjection, resultDirtyProjection));
}
void KisUpdateSchedulerTest::benchmarkOverlappedMerge()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
KisUpdateScheduler scheduler(image.data());
const qint32 xShift = 10;
const qint32 yShift = 0;
const qint32 numShifts = 64;
QBENCHMARK{
QRect dirtyRect(0, 0, 200, imageRect.height());
for(int i = 0; i < numShifts; i++) {
- // qDebug() << dirtyRect;
+ // dbgKrita << dirtyRect;
scheduler.updateProjection(paintLayer1, dirtyRect, image->bounds());
dirtyRect.translate(xShift, yShift);
}
scheduler.waitForDone();
}
}
void KisUpdateSchedulerTest::testLocking()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
KisTestableUpdateScheduler scheduler(image.data(), 2);
QRect dirtyRect1(0,0,50,100);
QRect dirtyRect2(0,0,100,100);
QRect dirtyRect3(50,0,50,100);
QRect dirtyRect4(150,150,50,50);
KisTestableUpdaterContext *context = scheduler.updaterContext();
QVector<KisUpdateJobItem*> jobs;
scheduler.updateProjection(paintLayer1, imageRect, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), imageRect));
context->clear();
scheduler.lock();
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect2, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect3, imageRect);
scheduler.updateProjection(paintLayer1, dirtyRect4, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), false);
QCOMPARE(jobs[1]->isRunning(), false);
scheduler.unlock();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), true);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect2));
QVERIFY(checkWalker(jobs[1]->walker(), dirtyRect4));
}
void KisUpdateSchedulerTest::testExclusiveStrokes()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
QRect imageRect = image->bounds();
QCOMPARE(paintLayer1->name(), QString("paint1"));
QCOMPARE(imageRect, QRect(0,0,640,441));
QRect dirtyRect1(0,0,50,100);
KisTestableUpdateScheduler scheduler(image.data(), 2);
KisTestableUpdaterContext *context = scheduler.updaterContext();
QVector<KisUpdateJobItem*> jobs;
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
KisStrokeId id = scheduler.startStroke(new KisTestingStrokeStrategy("excl_", true, false));
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
context->clear();
scheduler.endStroke(id);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_init");
scheduler.updateProjection(paintLayer1, dirtyRect1, imageRect);
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_init");
context->clear();
scheduler.processQueues();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
COMPARE_NAME(jobs[0], "excl_finish");
context->clear();
scheduler.processQueues();
jobs = context->getJobs();
QCOMPARE(jobs[0]->isRunning(), true);
QCOMPARE(jobs[1]->isRunning(), false);
QVERIFY(checkWalker(jobs[0]->walker(), dirtyRect1));
}
void KisUpdateSchedulerTest::testEmptyStroke()
{
KisImageSP image = buildTestingImage();
KisStrokeId id = image->startStroke(new KisStrokeStrategy());
image->addJob(id, 0);
image->endStroke(id);
image->waitForDone();
}
#include "kis_lazy_wait_condition.h"
void KisUpdateSchedulerTest::testLazyWaitCondition()
{
{
- qDebug() << "Not initialized";
+ dbgKrita << "Not initialized";
KisLazyWaitCondition condition;
QVERIFY(!condition.wait(50));
}
{
- qDebug() << "Initialized, not awake";
+ dbgKrita << "Initialized, not awake";
KisLazyWaitCondition condition;
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.endWaiting();
}
{
- qDebug() << "Initialized, awake";
+ dbgKrita << "Initialized, awake";
KisLazyWaitCondition condition;
condition.initWaiting();
condition.wakeAll();
QVERIFY(condition.wait(50));
condition.endWaiting();
}
{
- qDebug() << "Initialized, not awake, then awake";
+ dbgKrita << "Initialized, not awake, then awake";
KisLazyWaitCondition condition;
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.wakeAll();
QVERIFY(condition.wait(50));
condition.endWaiting();
}
{
- qDebug() << "Doublewait";
+ dbgKrita << "Doublewait";
KisLazyWaitCondition condition;
condition.initWaiting();
condition.initWaiting();
QVERIFY(!condition.wait(50));
condition.wakeAll();
QVERIFY(condition.wait(50));
QVERIFY(condition.wait(50));
condition.endWaiting();
}
}
#define NUM_THREADS 10
#define NUM_CYCLES 500
#define NTH_CHECK 3
class UpdatesBlockTester : public QRunnable
{
public:
UpdatesBlockTester(KisUpdateScheduler *scheduler, KisNodeSP node)
: m_scheduler(scheduler), m_node(node)
{
}
void run() {
for (int i = 0; i < NUM_CYCLES; i++) {
if(i % NTH_CHECK == 0) {
m_scheduler->blockUpdates();
QTest::qSleep(1); // a bit of salt for crashiness ;)
Q_ASSERT(!m_scheduler->haveUpdatesRunning());
m_scheduler->unblockUpdates();
}
else {
QRect updateRect(0,0,100,100);
updateRect.moveTopLeft(QPoint((i%10)*100, (i%10)*100));
m_scheduler->updateProjection(m_node, updateRect, QRect(0,0,1100,1100));
}
}
}
private:
KisUpdateScheduler *m_scheduler;
KisNodeSP m_node;
};
void KisUpdateSchedulerTest::testBlockUpdates()
{
KisImageSP image = buildTestingImage();
KisNodeSP rootLayer = image->rootLayer();
KisNodeSP paintLayer1 = rootLayer->firstChild();
KisUpdateScheduler scheduler(image.data());
QThreadPool threadPool;
threadPool.setMaxThreadCount(NUM_THREADS);
for(int i = 0; i< NUM_THREADS; i++) {
UpdatesBlockTester *tester =
new UpdatesBlockTester(&scheduler, paintLayer1);
threadPool.start(tester);
}
threadPool.waitForDone();
}
#include "kis_update_time_monitor.h"
void KisUpdateSchedulerTest::testTimeMonitor()
{
QVector<QRect> dirtyRects;
KisUpdateTimeMonitor::instance()->startStrokeMeasure();
KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(100, 0));
KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 10);
QTest::qSleep(300);
KisUpdateTimeMonitor::instance()->reportJobStarted((void*) 20);
QTest::qSleep(100);
dirtyRects << QRect(10,10,10,10);
KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 10, dirtyRects);
QTest::qSleep(100);
dirtyRects.clear();
dirtyRects << QRect(30,30,10,10);
KisUpdateTimeMonitor::instance()->reportJobFinished((void*) 20, dirtyRects);
QTest::qSleep(500);
KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(10,10,10,10));
QTest::qSleep(300);
KisUpdateTimeMonitor::instance()->reportUpdateFinished(QRect(30,30,10,10));
KisUpdateTimeMonitor::instance()->reportMouseMove(QPointF(130, 0));
KisUpdateTimeMonitor::instance()->endStrokeMeasure();
}
QTEST_KDEMAIN(KisUpdateSchedulerTest, NoGUI)
diff --git a/krita/image/tests/kis_updater_context_test.cpp b/krita/image/tests/kis_updater_context_test.cpp
index 96ca3a5ce81..33c17deadd6 100644
--- a/krita/image/tests/kis_updater_context_test.cpp
+++ b/krita/image/tests/kis_updater_context_test.cpp
@@ -1,180 +1,180 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_updater_context_test.h"
#include <qtest_kde.h>
#include <QAtomicInt>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_paint_layer.h"
#include "kis_merge_walker.h"
#include "kis_updater_context.h"
#include "kis_image.h"
#include "scheduler_utils.h"
void KisUpdaterContextTest::testJobInterference()
{
KisTestableUpdaterContext context(3);
QRect imageRect(0,0,100,100);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8);
image->lock();
image->addNode(paintLayer);
QRect dirtyRect1(0,0,50,100);
KisBaseRectsWalkerSP walker1 = new KisMergeWalker(imageRect);
walker1->collectRects(paintLayer, dirtyRect1);
QRect dirtyRect2(30,0,100,100);
KisBaseRectsWalkerSP walker2 = new KisMergeWalker(imageRect);
walker2->collectRects(paintLayer, dirtyRect2);
context.lock();
context.addMergeJob(walker1);
QVERIFY(!context.isJobAllowed(walker2));
context.unlock();
}
void KisUpdaterContextTest::testSnapshot()
{
KisTestableUpdaterContext context(3);
QRect imageRect(0,0,100,100);
KisBaseRectsWalkerSP walker1 = new KisMergeWalker(imageRect);
qint32 numMergeJobs = -777;
qint32 numStrokeJobs = -777;
context.lock();
context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
QCOMPARE(numMergeJobs, 0);
QCOMPARE(numStrokeJobs, 0);
context.addMergeJob(walker1);
context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
QCOMPARE(numMergeJobs, 1);
QCOMPARE(numStrokeJobs, 0);
KisStrokeJobData *data =
new KisStrokeJobData(KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
KisStrokeJobStrategy *strategy = new KisNoopDabStrategy("test");
context.addStrokeJob(new KisStrokeJob(strategy, data));
context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
QCOMPARE(numMergeJobs, 1);
QCOMPARE(numStrokeJobs, 1);
context.addSpontaneousJob(new KisNoopSpontaneousJob());
context.getJobsSnapshot(numMergeJobs, numStrokeJobs);
QCOMPARE(numMergeJobs, 2);
QCOMPARE(numStrokeJobs, 1);
context.unlock();
}
#define NUM_THREADS 10
#define NUM_JOBS 6000
#define EXCLUSIVE_NTH 3
#define NUM_CHECKS 10
#define CHECK_DELAY 3 // ms
class ExclusivenessCheckerStrategy : public KisStrokeJobStrategy
{
public:
ExclusivenessCheckerStrategy(QAtomicInt &counter,
QAtomicInt &hadConcurrency)
: m_counter(counter),
m_hadConcurrency(hadConcurrency)
{
}
void run(KisStrokeJobData *data) {
Q_UNUSED(data);
m_counter.ref();
for(int i = 0; i < NUM_CHECKS; i++) {
if(data->isExclusive()) {
Q_ASSERT(m_counter == 1);
}
else if (m_counter > 1) {
m_hadConcurrency.ref();
}
QTest::qSleep(CHECK_DELAY);
}
m_counter.deref();
}
private:
QAtomicInt &m_counter;
QAtomicInt &m_hadConcurrency;
};
void KisUpdaterContextTest::stressTestExclusiveJobs()
{
KisUpdaterContext context(NUM_THREADS);
QAtomicInt counter;
QAtomicInt hadConcurrency;
for(int i = 0; i < NUM_JOBS; i++) {
if(context.hasSpareThread()) {
bool isExclusive = i % EXCLUSIVE_NTH == 0;
KisStrokeJobData *data =
new KisStrokeJobData(KisStrokeJobData::SEQUENTIAL,
isExclusive ?
KisStrokeJobData::EXCLUSIVE :
KisStrokeJobData::NORMAL);
KisStrokeJobStrategy *strategy =
new ExclusivenessCheckerStrategy(counter, hadConcurrency);
context.addStrokeJob(new KisStrokeJob(strategy, data));
}
else {
QTest::qSleep(CHECK_DELAY);
}
}
context.waitForDone();
QVERIFY(!counter);
- qDebug() << "Concurrency observed:" << hadConcurrency
+ dbgKrita << "Concurrency observed:" << hadConcurrency
<< "/" << NUM_CHECKS * NUM_JOBS;
}
QTEST_KDEMAIN(KisUpdaterContextTest, NoGUI)
diff --git a/krita/image/tests/kis_walkers_test.cpp b/krita/image/tests/kis_walkers_test.cpp
index 09acf67b36d..a84eae7342a 100644
--- a/krita/image/tests/kis_walkers_test.cpp
+++ b/krita/image/tests/kis_walkers_test.cpp
@@ -1,1228 +1,1228 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_walkers_test.h"
#include "kis_base_rects_walker.h"
#include "kis_merge_walker.h"
#include "kis_refresh_subtree_walker.h"
#include "kis_full_refresh_walker.h"
#include <qtest_kde.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include "kis_image.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_clone_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_selection.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_filter_mask.h"
#include "kis_transparency_mask.h"
#define DEBUG_VISITORS
QString nodeTypeString(KisMergeWalker::NodePosition position);
QString nodeTypePostfix(KisMergeWalker::NodePosition position);
/************** Test Implementation Of A Walker *********************/
class KisTestWalker : public KisMergeWalker
{
public:
KisTestWalker()
:KisMergeWalker(QRect())
{
}
QStringList popResult() {
QStringList order(m_order);
m_order.clear();
return order;
}
using KisMergeWalker::startTrip;
protected:
void registerChangeRect(KisProjectionLeafSP node, NodePosition position) {
QString postfix;
if(!node->isLayer()) {
postfix = "[skipped as not-a-layer]";
}
#ifdef DEBUG_VISITORS
- qDebug()<< "FW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
+ dbgKrita<< "FW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
#endif
if(postfix.isEmpty()) {
m_order.append(node->node()->name());
}
}
void registerNeedRect(KisProjectionLeafSP node, NodePosition position) {
QString postfix;
if(!node->isLayer()) {
postfix = "[skipped as not-a-layer]";
}
#ifdef DEBUG_VISITORS
- qDebug()<< "BW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
+ dbgKrita<< "BW:"<< node->node()->name() <<'\t'<< nodeTypeString(position) << postfix;
#endif
if(postfix.isEmpty()) {
m_order.append(node->node()->name() + nodeTypePostfix(position));
}
}
protected:
QStringList m_order;
};
/************** Debug And Verify Code *******************************/
struct UpdateTestJob {
QString updateAreaName;
KisNodeSP startNode;
QRect updateRect;
QString referenceString;
QRect accessRect;
bool changeRectVaries;
bool needRectVaries;
};
void reportStartWith(QString nodeName, QRect rect = QRect())
{
- qDebug();
+ dbgKrita;
if(!rect.isEmpty())
- qDebug() << "Start with:" << nodeName << rect;
+ dbgKrita << "Start with:" << nodeName << rect;
else
- qDebug() << "Start with:" << nodeName;
+ dbgKrita << "Start with:" << nodeName;
}
QString nodeTypeString(KisMergeWalker::NodePosition position)
{
QString string;
if(position & KisMergeWalker::N_TOPMOST)
string="TOP";
else if(position & KisMergeWalker::N_BOTTOMMOST)
string="BOT";
else
string="NOR";
if(position & KisMergeWalker::N_ABOVE_FILTHY)
string+="_ABOVE ";
else if(position & KisMergeWalker::N_FILTHY)
string+="_FILTH*";
else if(position & KisMergeWalker::N_FILTHY_PROJECTION)
string+="_PROJE*";
else if(position & KisMergeWalker::N_FILTHY_ORIGINAL)
string+="_ORIGI*_WARNINIG!!!: NOT USED";
else if(position & KisMergeWalker::N_BELOW_FILTHY)
string+="_BELOW ";
else if(position & KisMergeWalker::N_EXTRA)
string+="_EXTRA*";
else
qFatal("Impossible happened");
return string;
}
QString nodeTypePostfix(KisMergeWalker::NodePosition position)
{
QString string('_');
if(position & KisMergeWalker::N_TOPMOST)
string += 'T';
else if(position & KisMergeWalker::N_BOTTOMMOST)
string += 'B';
else
string += 'N';
if(position & KisMergeWalker::N_ABOVE_FILTHY)
string += 'A';
else if(position & KisMergeWalker::N_FILTHY)
string += 'F';
else if(position & KisMergeWalker::N_FILTHY_PROJECTION)
string += 'P';
else if(position & KisMergeWalker::N_FILTHY_ORIGINAL)
string += 'O';
else if(position & KisMergeWalker::N_BELOW_FILTHY)
string += 'B';
else if(position & KisMergeWalker::N_EXTRA)
string += 'E';
else
qFatal("Impossible happened");
return string;
}
void KisWalkersTest::verifyResult(KisBaseRectsWalker &walker, struct UpdateTestJob &job)
{
QStringList list;
if(!job.referenceString.isEmpty()) {
list = job.referenceString.split(',');
}
verifyResult(walker, list, job.accessRect,
job.changeRectVaries, job.needRectVaries);
}
void KisWalkersTest::verifyResult(KisBaseRectsWalker &walker, QStringList reference,
QRect accessRect, bool changeRectVaries,
bool needRectVaries)
{
KisMergeWalker::LeafStack &list = walker.leafStack();
QStringList::const_iterator iter = reference.constBegin();
if(reference.size() != list.size()) {
- qDebug() << "*** Seems like the walker returned stack of wrong size"
+ dbgKrita << "*** Seems like the walker returned stack of wrong size"
<< "( ref:" << reference.size() << "act:" << list.size() << ")";
- qDebug() << "*** We are going to crash soon... just wait...";
+ dbgKrita << "*** We are going to crash soon... just wait...";
}
foreach(const KisMergeWalker::JobItem &item, list) {
#ifdef DEBUG_VISITORS
- qDebug() << item.m_leaf->node()->name() << '\t'
+ dbgKrita << item.m_leaf->node()->name() << '\t'
<< item.m_applyRect << '\t'
<< nodeTypeString(item.m_position);
#endif
QCOMPARE(item.m_leaf->node()->name(), *iter);
iter++;
}
#ifdef DEBUG_VISITORS
- qDebug() << "Result AR:\t" << walker.accessRect();
+ dbgKrita << "Result AR:\t" << walker.accessRect();
#endif
QCOMPARE(walker.accessRect(), accessRect);
QCOMPARE(walker.changeRectVaries(), changeRectVaries);
QCOMPARE(walker.needRectVaries(), needRectVaries);
}
/************** Actual Testing **************************************/
/*
+----------+
|root |
| layer 5 |
| group |
| paint 4 |
| paint 3 |
| adj |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testUsualVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(adjustmentLayer, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
KisTestWalker walker;
{
QString order("paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NF,adj_NB,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("paint3");
walker.startTrip(paintLayer3->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("adj,paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NA,adj_NF,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("adj");
walker.startTrip(adjustmentLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB");
QStringList orderList = order.split(',');
reportStartWith("group");
walker.startTrip(groupLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
}
/*
+----------+
|root |
| layer 5 |
| group |
| mask 1 |
| paint 4 |
| paint 3 |
| adj |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testVisitingWithTopmostMask()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
KisFilterMaskSP filterMask1 = new KisFilterMask();
filterMask1->initSelection(groupLayer);
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration1 = filter->defaultConfiguration(0);
filterMask1->setFilter(configuration1);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(adjustmentLayer, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
// nasty mask!
image->addNode(filterMask1, groupLayer);
/**
* The results must be the same as for testUsualVisiting
*/
KisTestWalker walker;
{
QString order("paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NF,adj_NB,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("paint3");
walker.startTrip(paintLayer3->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("adj,paint3,paint4,group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB,"
"paint4_TA,paint3_NA,adj_NF,paint2_BB");
QStringList orderList = order.split(',');
reportStartWith("adj");
walker.startTrip(adjustmentLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
{
QString order("group,paint5,root,"
"root_TF,paint5_TA,group_NF,paint1_BB");
QStringList orderList = order.split(',');
reportStartWith("group");
walker.startTrip(groupLayer->projectionLeaf());
QVERIFY(walker.popResult() == orderList);
}
}
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testMergeVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-7,-7,44,44);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-10,-10,50,50);
reportStartWith("paint2");
walker.collectRects(paintLayer2, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cplx2,group,paint1");
QStringList orderList = order.split(',');
QRect accessRect(3,3,24,24);
reportStartWith("paint5");
walker.collectRects(paintLayer5, testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
{
/**
* Test cropping
*/
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("paint2 (with cropping)");
walker.setCropRect(image->bounds());
walker.collectRects(paintLayer2, testRect);
walker.setCropRect(cropRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
/**
* Test uncropped values
*/
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect cropRect(9,9,12,12);
QRect accessRect(cropRect);
reportStartWith("paint2 (testing uncropped)");
walker.setCropRect(cropRect);
walker.collectRects(paintLayer2, testRect);
walker.setCropRect(cropRect);
verifyResult(walker, orderList, accessRect, true, false);
QCOMPARE(walker.uncroppedChangeRect(), QRect(4,4,22,22));
}
}
/*
+------------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| cplxacc 1 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+------------+
*/
void KisWalkersTest::testComplexAccessVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
KisLayerSP complexAccess = new ComplexAccessLayer(image, "cplxacc1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(complexAccess, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,cplxacc1,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect = QRect(-7,-7,44,44) | QRect(0,0,30,30).translated(70,0);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
void KisWalkersTest::checkNotification(const KisMergeWalker::CloneNotification &notification,
const QString &name,
const QRect &rect)
{
QCOMPARE(notification.m_layer->name(), name);
QCOMPARE(notification.m_dirtyRect, rect);
}
/*
+--------------+
|root |
| paint 3 <--+ |
| cplx 2 | |
| group <--+ | |
| cplx 1 | | |
| paint 2 | | |
| clone 2 -+ | |
| clone 1 ---+ |
| paint 1 |
+--------------+
*/
void KisWalkersTest::testCloneNotificationsVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer3, image, "clone1", OPACITY_OPAQUE_U8);
KisLayerSP cloneLayer2 = new KisCloneLayer(groupLayer, image, "clone2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(cloneLayer1, image->rootLayer());
image->addNode(cloneLayer2, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer3, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
QRect testRect(10,10,10,10);
QRect cropRect(5,5,507,507);
KisMergeWalker walker(cropRect);
{
QString order("root,paint3,cplx2,group,clone2,clone1,paint1,"
"cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect = QRect(5,5,35,35);
reportStartWith("paint2");
walker.collectRects(paintLayer2, testRect);
verifyResult(walker, orderList, accessRect, true, true);
const KisMergeWalker::CloneNotificationsVector vector = walker.cloneNotifications();
QCOMPARE(vector.size(), 1);
checkNotification(vector[0], "group", QRect(7,7,16,16));
}
}
class TestingRefreshSubtreeWalker : public KisRefreshSubtreeWalker
{
public:
TestingRefreshSubtreeWalker(QRect cropRect) : KisRefreshSubtreeWalker(cropRect) {}
UpdateType type() const { return FULL_REFRESH; }
};
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testRefreshSubtreeVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
TestingRefreshSubtreeWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-10,-10,50,50);
reportStartWith("root");
walker.collectRects(image->rootLayer(), testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
/*
+----------+
|root |
| layer 5 |
| cplx 2 |
| group |
| paint 4 |
| paint 3 |
| cplx 1 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testFullRefreshVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer1 = new ComplexRectsLayer(image, "cplx1", OPACITY_OPAQUE_U8);
KisLayerSP complexRectsLayer2 = new ComplexRectsLayer(image, "cplx2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(complexRectsLayer2, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(complexRectsLayer1, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisFullRefreshWalker walker(cropRect);
{
QString order("root,paint5,cplx2,group,paint1,"
"group,paint4,paint3,cplx1,paint2");
QStringList orderList = order.split(',');
QRect accessRect(-10,-10,50,50);
reportStartWith("root");
walker.collectRects(groupLayer, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
}
/*
+----------+
|root |
| layer 5 |
| cache1 |
| group |
| paint 4 |
| paint 3 |
| paint 2 |
| paint 1 |
+----------+
*/
void KisWalkersTest::testCachedVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
KisLayerSP cacheLayer1 = new CacheLayer(image, "cache1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(groupLayer, image->rootLayer());
image->addNode(cacheLayer1, image->rootLayer());
image->addNode(paintLayer5, image->rootLayer());
image->addNode(paintLayer2, groupLayer);
image->addNode(paintLayer3, groupLayer);
image->addNode(paintLayer4, groupLayer);
QRect testRect(10,10,10,10);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint5,cache1,group,paint1,"
"paint4,paint3,paint2");
QStringList orderList = order.split(',');
QRect accessRect(0,0,30,30);
reportStartWith("paint3");
walker.collectRects(paintLayer3, testRect);
verifyResult(walker, orderList, accessRect, true, true);
}
{
QString order("root,paint5,cache1");
QStringList orderList = order.split(',');
QRect accessRect(10,10,10,10);
reportStartWith("paint5");
walker.collectRects(paintLayer5, testRect);
verifyResult(walker, orderList, accessRect, false, true);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksVisiting()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration1 = filter->defaultConfiguration(0);
KisFilterConfiguration *configuration2 = filter->defaultConfiguration(0);
filterMask1->setFilter(configuration1);
filterMask2->setFilter(configuration2);
QRect selection1(10, 10, 20, 10);
QRect selection2(30, 15, 10, 10);
QRect selection3(20, 10, 20, 10);
filterMask1->testingInitSelection(selection1);
transparencyMask->testingInitSelection(selection2);
filterMask2->testingInitSelection(selection3);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
QRect testRect(5,5,30,30);
// Empty rect to show we don't need any cropping
QRect cropRect;
KisMergeWalker walker(cropRect);
{
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("tmask");
walker.collectRects(transparencyMask, testRect);
verifyResult(walker, orderList, accessRect, true, false);
}
KisTestWalker twalker;
{
QString order("paint2,root,"
"root_TF,paint2_TA,paint1_BP");
QStringList orderList = order.split(',');
reportStartWith("tmask");
twalker.startTrip(transparencyMask->projectionLeaf());
QCOMPARE(twalker.popResult(), orderList);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksVisitingNoFilthy()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration1 = filter->defaultConfiguration(0);
KisFilterConfiguration *configuration2 = filter->defaultConfiguration(0);
filterMask1->setFilter(configuration1);
filterMask2->setFilter(configuration2);
QRect selection1(10, 10, 20, 10);
QRect selection2(30, 15, 10, 10);
QRect selection3(20, 10, 20, 10);
filterMask1->testingInitSelection(selection1);
transparencyMask->testingInitSelection(selection2);
filterMask2->testingInitSelection(selection3);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
QRect testRect(5,5,30,30);
// Empty rect to show we don't need any cropping
QRect cropRect;
{
KisMergeWalker walker(cropRect, KisMergeWalker::NO_FILTHY);
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(0,0,40,40);
reportStartWith("tmask");
walker.collectRects(transparencyMask, testRect);
verifyResult(walker, orderList, accessRect, true, false);
}
{
KisMergeWalker walker(cropRect, KisMergeWalker::NO_FILTHY);
QString order("root,paint2,paint1");
QStringList orderList = order.split(',');
QRect accessRect(5,5,30,30);
reportStartWith("paint1");
walker.collectRects(paintLayer1, testRect);
verifyResult(walker, orderList, accessRect, false, false);
}
}
/*
+----------+
|root |
| paint 2 |
| paint 1 |
| fmask2 |
| tmask |
| fmask1 |
+----------+
*/
void KisWalkersTest::testMasksOverlapping()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, 512, 512, colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
KisFilterMaskSP filterMask1 = new KisFilterMask();
KisFilterMaskSP filterMask2 = new KisFilterMask();
KisTransparencyMaskSP transparencyMask = new KisTransparencyMask();
KisFilterSP blurFilter = KisFilterRegistry::instance()->value("blur");
KisFilterSP invertFilter = KisFilterRegistry::instance()->value("invert");
Q_ASSERT(blurFilter);
Q_ASSERT(invertFilter);
KisFilterConfiguration *blurConfiguration = blurFilter->defaultConfiguration(0);
KisFilterConfiguration *invertConfiguration = invertFilter->defaultConfiguration(0);
filterMask1->setFilter(invertConfiguration);
filterMask2->setFilter(blurConfiguration);
QRect selection1(0, 0, 128, 128);
QRect selection2(128, 0, 128, 128);
QRect selection3(0, 64, 256, 128);
filterMask1->testingInitSelection(selection1);
transparencyMask->testingInitSelection(selection2);
filterMask2->testingInitSelection(selection3);
image->addNode(filterMask1, paintLayer1);
image->addNode(transparencyMask, paintLayer1);
image->addNode(filterMask2, paintLayer1);
// Empty rect to show we don't need any cropping
QRect cropRect;
QRect IMRect(10,10,50,50);
QRect TMRect(135,10,40,40);
QRect IMTMRect(10,10,256,40);
QList<UpdateTestJob> updateList;
{
// FIXME: now we do not crop change rect if COMPOSITE_OVER is used!
UpdateTestJob job = {"IM", paintLayer1, IMRect,
"",
QRect(0,0,0,0), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", filterMask1, IMRect,
"",
QRect(0,0,0,0), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", transparencyMask, IMRect,
"root,paint2,paint1",
QRect(5,10,60,55), true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IM", filterMask2, IMRect,
"root,paint2,paint1",
IMRect, false, false};
updateList.append(job);
}
/******* Dirty rect: transparency mask *********/
{
UpdateTestJob job = {"TM", paintLayer1, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", filterMask1, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", transparencyMask, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"TM", filterMask2, TMRect,
"root,paint2,paint1",
TMRect, false, false};
updateList.append(job);
}
/******* Dirty rect: invert + transparency mask *********/
{
UpdateTestJob job = {"IMTM", paintLayer1, IMTMRect,
"root,paint2,paint1",
IMTMRect & selection2, true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", filterMask1, IMTMRect,
"root,paint2,paint1",
IMTMRect & selection2, true, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", transparencyMask, IMTMRect,
"root,paint2,paint1",
IMTMRect, false, false};
updateList.append(job);
}
{
UpdateTestJob job = {"IMTM", filterMask2, IMTMRect,
"root,paint2,paint1",
IMTMRect, false, false};
updateList.append(job);
}
foreach(UpdateTestJob job, updateList) {
KisMergeWalker walker(cropRect);
reportStartWith(job.startNode->name(), job.updateRect);
- qDebug() << "Area:" << job.updateAreaName;
+ dbgKrita << "Area:" << job.updateAreaName;
walker.collectRects(job.startNode, job.updateRect);
verifyResult(walker, job);
}
}
/*
+----------+
|root |
| adj |
| paint 1 |
+----------+
*/
void KisWalkersTest::testRectsChecksum()
{
QRect imageRect(0,0,512,512);
QRect dirtyRect(100,100,100,100);
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisAdjustmentLayerSP adjustmentLayer = new KisAdjustmentLayer(image, "adj", 0, 0);
image->lock();
image->addNode(paintLayer1, image->rootLayer());
image->addNode(adjustmentLayer, image->rootLayer());
image->unlock();
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration;
KisMergeWalker walker(imageRect);
walker.collectRects(adjustmentLayer, dirtyRect);
QCOMPARE(walker.checksumValid(), true);
configuration = filter->defaultConfiguration(0);
adjustmentLayer->setFilter(configuration);
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
configuration = filter->defaultConfiguration(0);
configuration->setProperty("halfWidth", 20);
configuration->setProperty("halfHeight", 20);
adjustmentLayer->setFilter(configuration);
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
configuration = filter->defaultConfiguration(0);
configuration->setProperty("halfWidth", 21);
configuration->setProperty("halfHeight", 21);
adjustmentLayer->setFilter(configuration);
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
}
void KisWalkersTest::testGraphStructureChecksum()
{
QRect imageRect(0,0,512,512);
QRect dirtyRect(100,100,100,100);
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "walker test");
KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
image->lock();
image->addNode(paintLayer1, image->rootLayer());
image->unlock();
KisMergeWalker walker(imageRect);
walker.collectRects(paintLayer1, dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->lock();
image->addNode(paintLayer2, image->rootLayer());
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->lock();
image->moveNode(paintLayer1, image->rootLayer(), paintLayer2);
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
image->lock();
image->removeNode(paintLayer1);
image->unlock();
QCOMPARE(walker.checksumValid(), false);
walker.recalculate(dirtyRect);
QCOMPARE(walker.checksumValid(), true);
}
QTEST_KDEMAIN(KisWalkersTest, NoGUI)
diff --git a/krita/image/tests/kis_warp_transform_worker_test.cpp b/krita/image/tests/kis_warp_transform_worker_test.cpp
index 9933ea3e279..7cb5634e263 100644
--- a/krita/image/tests/kis_warp_transform_worker_test.cpp
+++ b/krita/image/tests/kis_warp_transform_worker_test.cpp
@@ -1,328 +1,328 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_warp_transform_worker_test.h"
#include <qtest_kde.h>
#include "testutil.h"
#include "kis_warptransform_worker.h"
#include <KoProgressUpdater.h>
void KisWarpTransformWorkerTest::test()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
// QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png"));
QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
qreal alpha = 1.0;
QRectF bounds(dev->exactBounds());
origPoints << bounds.topLeft();
origPoints << bounds.topRight();
origPoints << bounds.bottomRight();
origPoints << bounds.bottomLeft();
origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0);
transfPoints << bounds.topLeft();
transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft());
transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft());
transfPoints << bounds.bottomLeft() + QPointF(200, 0);
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20);
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20);
KisWarpTransformWorker worker(KisWarpTransformWorker::RIGID_TRANSFORM,
dev,
origPoints,
transfPoints,
alpha,
updater);
QBENCHMARK_ONCE {
worker.run();
}
QImage result = dev->convertToQImage(0);
TestUtil::checkQImage(result, "warp_transform_test", "simple", "tr");
}
void KisWarpTransformWorkerTest::testQImage()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
// QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png"));
QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png"));
image = image.convertToFormat(QImage::Format_ARGB32);
- qDebug() << ppVar(image.format());
+ dbgKrita << ppVar(image.format());
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
qreal alpha = 1.0;
QRectF bounds(image.rect());
origPoints << bounds.topLeft();
origPoints << bounds.topRight();
origPoints << bounds.bottomRight();
origPoints << bounds.bottomLeft();
origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0);
transfPoints << bounds.topLeft();
transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft());
transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft());
transfPoints << bounds.bottomLeft() + QPointF(200, 0);
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20);
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20);
QImage result;
QPointF newOffset;
QBENCHMARK_ONCE {
result = KisWarpTransformWorker::transformQImage(
KisWarpTransformWorker::RIGID_TRANSFORM,
origPoints, transfPoints, alpha,
image, QPointF(), &newOffset);
}
- qDebug() << ppVar(newOffset);
+ dbgKrita << ppVar(newOffset);
TestUtil::checkQImage(result, "warp_transform_test", "qimage", "tr");
}
#include "kis_four_point_interpolator_forward.h"
void KisWarpTransformWorkerTest::testForwardInterpolator()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst;
dst << QPointF(0, 0);
dst << QPointF(100, 10);
dst << QPointF(100, 120);
dst << QPointF(0, 100);
KisFourPointInterpolatorForward interp(src, dst);
QCOMPARE(interp.map(QPointF(0,50)), QPointF(0,50));
QCOMPARE(interp.map(QPointF(50,0)), QPointF(50,5));
QCOMPARE(interp.map(QPointF(100,0)), QPointF(100,10));
QCOMPARE(interp.map(QPointF(100,50)), QPointF(100,65));
QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,120));
QCOMPARE(interp.map(QPointF(50,100)), QPointF(50,110));
QCOMPARE(interp.map(QPointF(50,50)), QPointF(50,57.5));
}
#include "kis_four_point_interpolator_backward.h"
void KisWarpTransformWorkerTest::testBackwardInterpolatorXShear()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst;
dst << QPointF(0, 0);
dst << QPointF(100, 0);
dst << QPointF(120, 100);
dst << QPointF(10, 100);
KisFourPointInterpolatorBackward interp(src, dst);
QCOMPARE(interp.map(QPointF(10,100)), QPointF(0,100));
QCOMPARE(interp.map(QPointF(5,50)), QPointF(0,50));
QCOMPARE(interp.map(QPointF(110,50)), QPointF(100,50));
QCOMPARE(interp.map(QPointF(57.5,50)), QPointF(50,50));
}
void KisWarpTransformWorkerTest::testBackwardInterpolatorYShear()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst;
dst << QPointF(0, 0);
dst << QPointF(100, 10);
dst << QPointF(100, 120);
dst << QPointF(0, 100);
KisFourPointInterpolatorBackward interp(src, dst);
QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0));
QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0));
QCOMPARE(interp.map(QPointF(50,110)), QPointF(50,100));
QCOMPARE(interp.map(QPointF(50,57.5)), QPointF(50,50));
}
void KisWarpTransformWorkerTest::testBackwardInterpolatorXYShear()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst;
dst << QPointF(0, 0);
dst << QPointF(100, 10);
dst << QPointF(140, 120);
dst << QPointF(20, 100);
KisFourPointInterpolatorBackward interp(src, dst);
QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0));
QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0));
QCOMPARE(interp.map(QPointF(80,110)), QPointF(50,100));
QCOMPARE(interp.map(QPointF(120,65)), QPointF(100,50));
QCOMPARE(interp.map(QPointF(10,50)), QPointF(0,50));
}
void KisWarpTransformWorkerTest::testBackwardInterpolatorRoundTrip()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst;
dst << QPointF(100, 100);
dst << QPointF(20, 140);
dst << QPointF(10, 80);
dst << QPointF(15, 5);
KisFourPointInterpolatorForward f(src, dst);
KisFourPointInterpolatorBackward b(src, dst);
for (qreal y = 0; y <= 100; y += 1.0) {
for (qreal x = 0; x <= 100; x += 1.0) {
QPointF pt(x, y);
QPointF fwdPt = f.map(pt);
QPointF bwdPt = b.map(fwdPt);
- //qDebug() << "R:" << ppVar(pt) << ppVar(fwdPt) << ppVar(bwdPt) << (bwdPt - pt);
+ //dbgKrita << "R:" << ppVar(pt) << ppVar(fwdPt) << ppVar(bwdPt) << (bwdPt - pt);
QVERIFY((bwdPt - pt).manhattanLength() < 1e-3);
}
}
}
#include "kis_grid_interpolation_tools.h"
void KisWarpTransformWorkerTest::testGridSize()
{
QCOMPARE(GridIterationTools::calcGridDimension(1, 7, 4), 3);
QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3);
QCOMPARE(GridIterationTools::calcGridDimension(1, 9, 4), 4);
QCOMPARE(GridIterationTools::calcGridDimension(0, 7, 4), 3);
QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3);
QCOMPARE(GridIterationTools::calcGridDimension(4, 9, 4), 3);
QCOMPARE(GridIterationTools::calcGridDimension(0, 9, 4), 4);
QCOMPARE(GridIterationTools::calcGridDimension(-1, 9, 4), 5);
QCOMPARE(GridIterationTools::calcGridDimension(0, 300, 8), 39);
}
void KisWarpTransformWorkerTest::testBackwardInterpolatorExtrapolation()
{
QPolygonF src;
src << QPointF(0, 0);
src << QPointF(100, 0);
src << QPointF(100, 100);
src << QPointF(0, 100);
QPolygonF dst(src);
std::rotate(dst.begin(), dst.begin() + 1, dst.end());
KisFourPointInterpolatorBackward interp(src, dst);
// standard checks
QCOMPARE(interp.map(QPointF(0,0)), QPointF(0,100));
QCOMPARE(interp.map(QPointF(100,0)), QPointF(0,0));
QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,0));
QCOMPARE(interp.map(QPointF(0,100)), QPointF(100,100));
// extrapolate!
QCOMPARE(interp.map(QPointF(-10,0)), QPointF(0,110));
QCOMPARE(interp.map(QPointF(0,-10)), QPointF(-10,100));
QCOMPARE(interp.map(QPointF(-10,-10)), QPointF(-10,110));
QCOMPARE(interp.map(QPointF(110,0)), QPointF(0,-10));
QCOMPARE(interp.map(QPointF(100,-10)), QPointF(-10,0));
QCOMPARE(interp.map(QPointF(110,-10)), QPointF(-10,-10));
QCOMPARE(interp.map(QPointF(110,100)), QPointF(100, -10));
QCOMPARE(interp.map(QPointF(100,110)), QPointF(110, 0));
QCOMPARE(interp.map(QPointF(110,110)), QPointF(110,-10));
QCOMPARE(interp.map(QPointF(-10,100)), QPointF(100, 110));
QCOMPARE(interp.map(QPointF(0,110)), QPointF(110, 100));
QCOMPARE(interp.map(QPointF(-10,110)), QPointF(110,110));
}
QTEST_KDEMAIN(KisWarpTransformWorkerTest, GUI)
diff --git a/krita/image/tests/scheduler_utils.h b/krita/image/tests/scheduler_utils.h
index 7a6f31dde0c..7272aad7e45 100644
--- a/krita/image/tests/scheduler_utils.h
+++ b/krita/image/tests/scheduler_utils.h
@@ -1,177 +1,177 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SCHEDULER_UTILS_H
#define __SCHEDULER_UTILS_H
#include <QRect>
#include "kis_merge_walker.h"
#include "kis_stroke_strategy.h"
#include "kis_stroke_job.h"
#include "kis_spontaneous_job.h"
#include "kis_stroke.h"
#define SCOMPARE(s1, s2) QCOMPARE(QString(s1), QString(s2))
#define COMPARE_WALKER(item, walker) \
QCOMPARE(item->walker(), walker)
#define COMPARE_NAME(item, name) \
QCOMPARE(getJobName(item->strokeJob()), QString(name))
#define VERIFY_EMPTY(item) \
QVERIFY(!item->isRunning())
void executeStrokeJobs(KisStroke *stroke) {
KisStrokeJob *job;
while((job = stroke->popOneJob())) {
job->run();
delete job;
}
}
bool checkWalker(KisBaseRectsWalkerSP walker, const QRect &rect) {
if(walker->requestedRect() == rect) {
return true;
}
else {
- qDebug() << "walker rect:" << walker->requestedRect();
- qDebug() << "expected rect:" << rect;
+ dbgKrita << "walker rect:" << walker->requestedRect();
+ dbgKrita << "expected rect:" << rect;
return false;
}
}
class KisNoopSpontaneousJob : public KisSpontaneousJob
{
public:
KisNoopSpontaneousJob(bool overridesEverything = false)
: m_overridesEverything(overridesEverything)
{
}
void run() {
}
bool overrides(const KisSpontaneousJob *otherJob) {
Q_UNUSED(otherJob);
return m_overridesEverything;
}
private:
bool m_overridesEverything;
};
class KisNoopDabStrategy : public KisStrokeJobStrategy
{
public:
KisNoopDabStrategy(QString name)
: m_name(name),
m_isMarked(false)
{}
void run(KisStrokeJobData *data) {
Q_UNUSED(data);
}
QString name() {
return m_name;
}
void setMarked() {
m_isMarked = true;
}
bool isMarked() const {
return m_isMarked;
}
private:
QString m_name;
bool m_isMarked;
};
class KisTestingStrokeStrategy : public KisStrokeStrategy
{
public:
KisTestingStrokeStrategy(const QString &prefix = QString(),
bool exclusive = false,
bool inhibitServiceJobs = false)
: m_prefix(prefix),
m_inhibitServiceJobs(inhibitServiceJobs),
m_cancelSeqNo(0)
{
setExclusive(exclusive);
}
KisStrokeJobStrategy* createInitStrategy() {
return !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "init") : 0;
}
KisStrokeJobStrategy* createFinishStrategy() {
return !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "finish") : 0;
}
KisStrokeJobStrategy* createCancelStrategy() {
return !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "cancel") : 0;
}
KisStrokeJobStrategy* createDabStrategy() {
return new KisNoopDabStrategy(m_prefix + "dab");
}
class CancelData : public KisStrokeJobData
{
public:
CancelData(int seqNo) : m_seqNo(seqNo) {}
int seqNo() const { return m_seqNo; }
private:
int m_seqNo;
};
KisStrokeJobData* createCancelData() {
return new CancelData(m_cancelSeqNo++);
}
private:
QString m_prefix;
bool m_inhibitServiceJobs;
int m_cancelSeqNo;
};
inline QString getJobName(KisStrokeJob *job) {
KisNoopDabStrategy *pointer =
dynamic_cast<KisNoopDabStrategy*>(job->testingGetDabStrategy());
Q_ASSERT(pointer);
return pointer->name();
}
inline int cancelSeqNo(KisStrokeJob *job) {
KisTestingStrokeStrategy::CancelData *pointer =
dynamic_cast<KisTestingStrokeStrategy::CancelData*>
(job->testingGetDabData());
Q_ASSERT(pointer);
return pointer->seqNo();
}
#endif /* __SCHEDULER_UTILS_H */
diff --git a/krita/image/tiles3/kis_memento_item.h b/krita/image/tiles3/kis_memento_item.h
index e70a77edad8..6078caeb6ae 100644
--- a/krita/image/tiles3/kis_memento_item.h
+++ b/krita/image/tiles3/kis_memento_item.h
@@ -1,227 +1,227 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_MEMENTO_ITEM_H_
#define KIS_MEMENTO_ITEM_H_
#include <kis_shared.h>
#include <kis_shared_ptr.h>
#include "kis_tile_data_store.h"
#include "kis_tile.h"
class KisMementoItem;
typedef KisSharedPtr<KisMementoItem> KisMementoItemSP;
class KisMementoItem : public KisShared
{
public:
enum enumType {
CHANGED = 0x0,
DELETED = 0x1
};
public:
KisMementoItem()
: m_tileData(0), m_committedFlag(false) {
}
KisMementoItem(const KisMementoItem& rhs)
: KisShared(),
m_tileData(rhs.m_tileData),
m_committedFlag(rhs.m_committedFlag),
m_type(rhs.m_type),
m_col(rhs.m_col),
m_row(rhs.m_row),
m_next(0),
m_parent(0) {
if (m_tileData) {
if (m_committedFlag)
m_tileData->acquire();
else
m_tileData->ref();
}
}
/**
* Automatically called by Kis..HashTable. It means that
* this mementoItem is a root item of parental hierarchy.
* So m_parent should be NULL. Taking into account the tile
* was not present before, the status of the item
* should be 'DELETED'.
* This memmento item is considered as committed, so we acquire
* the tile data right at the beginning.
*/
KisMementoItem(qint32 col, qint32 row, KisTileData* defaultTileData, KisMementoManager *mm) {
Q_UNUSED(mm);
m_tileData = defaultTileData;
/* acquire the tileData deliberately and completely */
m_tileData->acquire();
m_col = col;
m_row = row;
m_type = DELETED;
m_parent = 0;
m_committedFlag = true; /* yes, we've committed it */
}
/**
* FIXME: Not sure this function has any particular usecase.
* Just leave it for compatibility with a hash table
*/
KisMementoItem(const KisMementoItem &rhs, KisMementoManager *mm) {
Q_UNUSED(mm);
m_tileData = rhs.m_tileData;
/* Setting counter: m_refCount++ */
m_tileData->ref();
m_col = rhs.m_col;
m_row = rhs.m_row;
m_type = CHANGED;
m_parent = 0;
m_committedFlag = false;
}
~KisMementoItem() {
releaseTileData();
}
void notifyDead() {
// just to resemple KisTile...
}
void reset() {
releaseTileData();
m_tileData = 0;
m_committedFlag = false;
}
void deleteTile(KisTile* tile, KisTileData* defaultTileData) {
m_tileData = defaultTileData;
/* Setting counter: m_refCount++ */
m_tileData->ref();
m_col = tile->col();
m_row = tile->row();
m_type = DELETED;
}
void changeTile(KisTile* tile) {
m_tileData = tile->tileData();
/* Setting counter: m_refCount++ */
m_tileData->ref();
m_col = tile->col();
m_row = tile->row();
m_type = CHANGED;
}
void commit() {
if (m_committedFlag) return;
if (m_tileData) {
/**
* Setting counters to proper values:
* m_refCount++, m_usersCount++;
* m_refCount--
*/
m_tileData->acquire();
m_tileData->deref();
m_tileData->setMementoed(true);
}
m_committedFlag = true;
}
inline KisTileSP tile(KisMementoManager *mm) {
Q_ASSERT(m_tileData);
return new KisTile(m_col, m_row, m_tileData, mm);
}
inline enumType type() {
return m_type;
}
inline void setParent(KisMementoItemSP parent) {
m_parent = parent;
}
inline KisMementoItemSP parent() {
return m_parent;
}
// Stuff for Kis..HashTable
inline void setNext(KisMementoItemSP next) {
m_next = next;
}
inline KisMementoItemSP next() const {
return m_next;
}
inline qint32 col() const {
return m_col;
}
inline qint32 row() const {
return m_row;
}
inline KisTileData* tileData() const {
return m_tileData;
}
void debugPrintInfo() {
QString s = QString("------\n"
"Memento item:\t\t0x%1 (0x%2)\n"
" status:\t(%3,%4) %5%6\n"
" parent:\t0x%7 (0x%8)\n"
" next:\t0x%9 (0x%10)\n")
.arg((quintptr)this)
.arg((quintptr)m_tileData)
.arg(m_col)
.arg(m_row)
.arg((m_type == CHANGED) ? 'W' : 'D')
.arg(m_committedFlag ? 'C' : '-')
.arg((quintptr)m_parent.data())
.arg(m_parent ? (quintptr)m_parent->m_tileData : 0)
.arg((quintptr)m_next.data())
.arg(m_next ? (quintptr)m_next->m_tileData : 0);
- qDebug() << s;
+ dbgKrita << s;
}
protected:
void releaseTileData() {
if (m_tileData) {
if (m_committedFlag) {
m_tileData->setMementoed(false);
m_tileData->release();
}
else {
m_tileData->deref();
}
}
}
protected:
KisTileData *m_tileData;
bool m_committedFlag;
enumType m_type;
qint32 m_col;
qint32 m_row;
KisMementoItemSP m_next;
KisMementoItemSP m_parent;
private:
};
#endif /* KIS_MEMENTO_ITEM_H_ */
diff --git a/krita/image/tiles3/kis_memory_pool.h b/krita/image/tiles3/kis_memory_pool.h
index c97fda60b82..6ba103712ce 100644
--- a/krita/image/tiles3/kis_memory_pool.h
+++ b/krita/image/tiles3/kis_memory_pool.h
@@ -1,135 +1,135 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_MEMORY_POOL_H
#define __KIS_MEMORY_POOL_H
#include "stdlib.h"
//#define DEBUG_HIT_MISS
#ifdef DEBUG_HIT_MISS
#include "kis_debug.h"
#define DECLARE_STATUS_VAR() qint64 m_hit; qint64 m_miss; qint64 m_averIndex; qint32 m_maxIndex
#define INIT_STATUS_VAR() m_hit=0; m_miss=0; m_averIndex=0; m_maxIndex=0
#define POOL_MISS() m_miss++
#define POOL_HIT(idx) m_hit++; m_averIndex+=idx; m_maxIndex=qMax(m_maxIndex, idx)
-#define REPORT_STATUS() qDebug() << ppVar(m_hit) << ppVar(m_miss) << "Hit rate:" << double(m_hit)/(m_hit+m_miss) << "Max index:" << m_maxIndex <<"Av. index:" << double(m_averIndex)/(m_hit)
+#define REPORT_STATUS() dbgKrita << ppVar(m_hit) << ppVar(m_miss) << "Hit rate:" << double(m_hit)/(m_hit+m_miss) << "Max index:" << m_maxIndex <<"Av. index:" << double(m_averIndex)/(m_hit)
#else
#define DECLARE_STATUS_VAR()
#define INIT_STATUS_VAR()
#define POOL_MISS()
#define POOL_HIT(idx)
#define REPORT_STATUS()
#endif
/**
* Memory pool object for fast allocation of big objects.
* Please, don't even try to use it for small objects -
* it's pointless. glibc will be faster anyway.
*
* What is small and what is big object? On my machine
* (2GiB of RAM) glibc's memory pool seemed to be about
* 128-256KiB. So if the total size of constantly
* allocated/deallocated objects grows higher 128KiB,
* you need to use this pool.
*
* In tests, there is no big difference between pools of
* size 32 and of size 64.
*
* How does it work? Very simple. The pool has an array
* of atomic pointers to memory chunks. When memory request
* comes, it searches in the array for the first non-zero
* pointer and rerurns it. When the pointer is returned back -
* the pool writes it to the first free slot.
*/
template<class T, int SIZE>
class KisMemoryPool
{
public:
KisMemoryPool() {
INIT_STATUS_VAR();
}
~KisMemoryPool() {
for(qint32 i = 0; i < SIZE; i++) {
free(m_array[i]);
}
REPORT_STATUS();
}
inline void push(void *ptr) {
if(m_allocated < SIZE) {
for(qint32 i = 0; i < SIZE; i++) {
if(m_array[i].testAndSetOrdered(0, ptr)) {
m_allocated.ref();
return;
}
}
}
free(ptr);
}
inline void* pop() {
if(m_allocated > 0) {
void *ptr;
for(qint32 i = 0; i < SIZE; i++) {
ptr = m_array[i].fetchAndStoreOrdered(0);
if (ptr) {
m_allocated.deref();
POOL_HIT(i);
return ptr;
}
}
}
POOL_MISS();
return malloc(sizeof(T));
}
private:
QAtomicPointer<void> m_array[SIZE];
QAtomicInt m_allocated;
DECLARE_STATUS_VAR()
};
#define POOL_OPERATORS(T) \
void* operator new(size_t size) { \
return size == sizeof(T) ? __m_pool.pop() : malloc(size); \
} \
void operator delete(void *ptr, size_t size) { \
if(size == sizeof(T)) __m_pool.push(ptr); \
else free(ptr); \
}
#define DECLARE_POOL(T,SIZE) \
static KisMemoryPool<T,SIZE> __m_pool
#define DEFINE_POOL(T,SIZE) \
KisMemoryPool<T,SIZE> T::__m_pool
#endif /* __KIS_MEMORY_POOL_H */
diff --git a/krita/image/tiles3/kis_random_accessor.cc b/krita/image/tiles3/kis_random_accessor.cc
index d904328325c..3c8a4fb7d5a 100644
--- a/krita/image/tiles3/kis_random_accessor.cc
+++ b/krita/image/tiles3/kis_random_accessor.cc
@@ -1,156 +1,156 @@
/*
* copyright (c) 2006,2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_random_accessor.h"
#include <kis_debug.h>
const quint32 KisRandomAccessor2::CACHESIZE = 4; // Define the number of tiles we keep in cache
KisRandomAccessor2::KisRandomAccessor2(KisTiledDataManager *ktm, qint32 x, qint32 y, qint32 offsetX, qint32 offsetY, bool writable) :
m_ktm(ktm),
m_tilesCache(new KisTileInfo*[CACHESIZE]),
m_tilesCacheSize(0),
m_pixelSize(m_ktm->pixelSize()),
m_writable(writable),
m_offsetX(offsetX),
m_offsetY(offsetY)
{
Q_ASSERT(ktm != 0);
moveTo(x, y);
}
KisRandomAccessor2::~KisRandomAccessor2()
{
for (uint i = 0; i < m_tilesCacheSize; i++) {
unlockTile(m_tilesCache[i]->tile);
unlockTile(m_tilesCache[i]->oldtile);
delete m_tilesCache[i];
}
delete [] m_tilesCache;
}
void KisRandomAccessor2::moveTo(qint32 x, qint32 y)
{
m_lastX = x;
m_lastY = y;
x -= m_offsetX;
y -= m_offsetY;
// Look in the cache if the tile if the data is available
for (uint i = 0; i < m_tilesCacheSize; i++) {
if (x >= m_tilesCache[i]->area_x1 && x <= m_tilesCache[i]->area_x2 &&
y >= m_tilesCache[i]->area_y1 && y <= m_tilesCache[i]->area_y2) {
KisTileInfo* kti = m_tilesCache[i];
quint32 offset = x - kti->area_x1 + (y - kti->area_y1) * KisTileData::WIDTH;
offset *= m_pixelSize;
m_data = kti->data + offset;
m_oldData = kti->oldData + offset;
if (i > 0) {
memmove(m_tilesCache + 1, m_tilesCache, i * sizeof(KisTileInfo*));
m_tilesCache[0] = kti;
}
return;
}
}
// The tile wasn't in cache
if (m_tilesCacheSize == KisRandomAccessor2::CACHESIZE) { // Remove last element of cache
unlockTile(m_tilesCache[CACHESIZE-1]->tile);
unlockTile(m_tilesCache[CACHESIZE-1]->oldtile);
delete m_tilesCache[CACHESIZE-1];
} else {
m_tilesCacheSize++;
}
quint32 col = xToCol(x);
quint32 row = yToRow(y);
KisTileInfo* kti = fetchTileData(col, row);
quint32 offset = x - kti->area_x1 + (y - kti->area_y1) * KisTileData::WIDTH;
offset *= m_pixelSize;
m_data = kti->data + offset;
m_oldData = kti->oldData + offset;
memmove(m_tilesCache + 1, m_tilesCache, (KisRandomAccessor2::CACHESIZE - 1) * sizeof(KisTileInfo*));
m_tilesCache[0] = kti;
}
quint8* KisRandomAccessor2::rawData()
{
return m_data;
}
const quint8* KisRandomAccessor2::oldRawData() const
{
#ifdef DEBUG
- kWarning(!m_ktm->hasCurrentMemento(), 41004) << "Accessing oldRawData() when no transaction is in progress.";
+ if (!m_ktm->hasCurrentMemento()) warnTiles << "Accessing oldRawData() when no transaction is in progress.";
#endif
return m_oldData;
}
const quint8* KisRandomAccessor2::rawDataConst() const
{
return m_data;
}
KisRandomAccessor2::KisTileInfo* KisRandomAccessor2::fetchTileData(qint32 col, qint32 row)
{
KisTileInfo* kti = new KisTileInfo;
kti->tile = m_ktm->getTile(col, row, m_writable);
lockTile(kti->tile);
kti->data = kti->tile->data();
kti->area_x1 = col * KisTileData::HEIGHT;
kti->area_y1 = row * KisTileData::WIDTH;
kti->area_x2 = kti->area_x1 + KisTileData::HEIGHT - 1;
kti->area_y2 = kti->area_y1 + KisTileData::WIDTH - 1;
// set old data
kti->oldtile = m_ktm->getOldTile(col, row);
lockOldTile(kti->oldtile);
kti->oldData = kti->oldtile->data();
return kti;
}
qint32 KisRandomAccessor2::numContiguousColumns(qint32 x) const
{
return m_ktm->numContiguousColumns(x - m_offsetX, 0, 0);
}
qint32 KisRandomAccessor2::numContiguousRows(qint32 y) const
{
return m_ktm->numContiguousRows(y - m_offsetY, 0, 0);
}
qint32 KisRandomAccessor2::rowStride(qint32 x, qint32 y) const
{
return m_ktm->rowStride(x - m_offsetX, y - m_offsetY);
}
qint32 KisRandomAccessor2::x() const
{
return m_lastX;
}
qint32 KisRandomAccessor2::y() const
{
return m_lastY;
}
diff --git a/krita/image/tiles3/kis_tile_data.cc b/krita/image/tiles3/kis_tile_data.cc
index f8ea577f9f4..47c8f23e31e 100644
--- a/krita/image/tiles3/kis_tile_data.cc
+++ b/krita/image/tiles3/kis_tile_data.cc
@@ -1,179 +1,179 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tile_data.h"
#include "kis_tile_data_store.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <boost/pool/singleton_pool.hpp>
// BPP == bytes per pixel
#define TILE_SIZE_4BPP (4 * __TILE_DATA_WIDTH * __TILE_DATA_HEIGHT)
#define TILE_SIZE_8BPP (8 * __TILE_DATA_WIDTH * __TILE_DATA_HEIGHT)
typedef boost::singleton_pool<KisTileData, TILE_SIZE_4BPP, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex, 256, 4096> BoostPool4BPP;
typedef boost::singleton_pool<KisTileData, TILE_SIZE_8BPP, boost::default_user_allocator_new_delete, boost::details::pool::default_mutex, 128, 2048> BoostPool8BPP;
const qint32 KisTileData::WIDTH = __TILE_DATA_WIDTH;
const qint32 KisTileData::HEIGHT = __TILE_DATA_HEIGHT;
KisTileData::KisTileData(qint32 pixelSize, const quint8 *defPixel, KisTileDataStore *store)
: m_state(NORMAL),
m_mementoFlag(0),
m_age(0),
m_usersCount(0),
m_refCount(0),
m_pixelSize(pixelSize),
m_store(store)
{
m_store->checkFreeMemory();
m_data = allocateData(m_pixelSize);
fillWithPixel(defPixel);
}
/**
* Duplicating tiledata
* + new object loaded in memory
* + it's unlocked and has refCount==0
*
* NOTE: the memory allocated by the pooler for clones is not counted
* by the store in memoryHardLimit. The pooler has it's own slice of
* memory and keeps track of the its size itself. So we should be able
* to disable the memory check with checkFreeMemory, otherwise, there
* is a deadlock.
*/
KisTileData::KisTileData(const KisTileData& rhs, bool checkFreeMemory)
: m_state(NORMAL),
m_mementoFlag(0),
m_age(0),
m_usersCount(0),
m_refCount(0),
m_pixelSize(rhs.m_pixelSize),
m_store(rhs.m_store)
{
if(checkFreeMemory) {
m_store->checkFreeMemory();
}
m_data = allocateData(m_pixelSize);
memcpy(m_data, rhs.data(), m_pixelSize * WIDTH * HEIGHT);
}
KisTileData::~KisTileData()
{
releaseMemory();
}
void KisTileData::fillWithPixel(const quint8 *defPixel)
{
quint8 *it = m_data;
for (int i = 0; i < WIDTH*HEIGHT; i++, it += m_pixelSize) {
memcpy(it, defPixel, m_pixelSize);
}
}
void KisTileData::releaseMemory()
{
if (m_data) {
freeData(m_data, m_pixelSize);
m_data = 0;
}
KisTileData *clone = 0;
while(m_clonesStack.pop(clone)) {
delete clone;
}
Q_ASSERT(m_clonesStack.isEmpty());
}
void KisTileData::allocateMemory()
{
Q_ASSERT(!m_data);
m_data = allocateData(m_pixelSize);
}
quint8* KisTileData::allocateData(const qint32 pixelSize)
{
quint8 *ptr = 0;
switch(pixelSize) {
case 4:
ptr = (quint8*)BoostPool4BPP::malloc();
break;
case 8:
ptr = (quint8*)BoostPool8BPP::malloc();
break;
default:
ptr = (quint8*) malloc(pixelSize * WIDTH * HEIGHT);
}
return ptr;
}
void KisTileData::freeData(quint8* ptr, const qint32 pixelSize)
{
switch(pixelSize) {
case 4:
BoostPool4BPP::free(ptr);
break;
case 8:
BoostPool8BPP::free(ptr);
break;
default:
free(ptr);
}
}
//#define DEBUG_POOL_RELEASE
#ifdef DEBUG_POOL_RELEASE
#include <unistd.h>
#endif /* DEBUG_POOL_RELEASE */
void KisTileData::releaseInternalPools()
{
if (!KisTileDataStore::instance()->numTiles() &&
!KisTileDataStore::instance()->numTilesInMemory()) {
BoostPool4BPP::purge_memory();
BoostPool8BPP::purge_memory();
#ifdef DEBUG_POOL_RELEASE
- qDebug() << "After purging unused memory:";
+ dbgKrita << "After purging unused memory:";
char command[256];
sprintf(command, "cat /proc/%d/status | grep -i vm", (int)getpid());
printf("--- %s ---\n", command);
(void)system(command);
#endif /* DEBUG_POOL_RELEASE */
}
// else {
-// qWarning() << "WARNING: trying to purge pool memory while there are used tiles present!";
-// qWarning() << " The memory will *NOT* be returned to the system, though it will";
-// qWarning() << " be reused by Krita internally. Please report to developers!";
+// warnKrita << "WARNING: trying to purge pool memory while there are used tiles present!";
+// warnKrita << " The memory will *NOT* be returned to the system, though it will";
+// warnKrita << " be reused by Krita internally. Please report to developers!";
// }
}
diff --git a/krita/image/tiles3/kis_tile_data_pooler.cc b/krita/image/tiles3/kis_tile_data_pooler.cc
index 11f8dfac555..7e20d95ba83 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.cc
+++ b/krita/image/tiles3/kis_tile_data_pooler.cc
@@ -1,397 +1,397 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include "kis_tile_data.h"
#include "kis_tile_data_store.h"
#include "kis_tile_data_store_iterators.h"
#include "kis_debug.h"
#include "kis_tile_data_pooler.h"
#include "kis_image_config.h"
const qint32 KisTileDataPooler::MAX_NUM_CLONES = 16;
const qint32 KisTileDataPooler::MAX_TIMEOUT = 60000; // 01m00s
const qint32 KisTileDataPooler::MIN_TIMEOUT = 100; // 00m00.100s
const qint32 KisTileDataPooler::TIMEOUT_FACTOR = 2;
//#define DEBUG_POOLER
#ifdef DEBUG_POOLER
#define DEBUG_CLONE_ACTION(td, numClones) \
printf("Cloned (%d):\t\t\t\t0x%X (clones: %d, users: %d, refs: %d)\n", \
numClones, td, td->m_clonesStack.size(), \
(int)td->m_usersCount, (int)td->m_refCount)
#define DEBUG_SIMPLE_ACTION(action) \
printf("pooler: %s\n", action)
#define RUNTIME_SANITY_CHECK(td) do { \
if(td->m_usersCount < td->m_refCount) { \
qDebug("**** Suspicious tiledata: 0x%X (clones: %d, users: %d, refs: %d) ****", \
td, td->m_clonesStack.size(), \
(int)td->m_usersCount, (int)td->m_refCount); \
} \
if(td->m_usersCount <= 0) { \
qFatal("pooler: Tiledata 0x%X has zero users counter. Crashing...", td); \
} \
} while(0) \
#define DEBUG_TILE_STATISTICS() debugTileStatistics()
#define DEBUG_LISTS(mem, beggers, beggersMem, donors, donorsMem) \
do { \
- qDebug() << "--- getLists finished ---"; \
- qDebug() << " memoryOccupied:" << mem << "/" << m_memoryLimit; \
- qDebug() << " donors:" << donors.size() \
+ dbgKrita << "--- getLists finished ---"; \
+ dbgKrita << " memoryOccupied:" << mem << "/" << m_memoryLimit; \
+ dbgKrita << " donors:" << donors.size() \
<< "(mem:" << donorsMem << ")"; \
- qDebug() << " beggers:" << beggers.size() \
+ dbgKrita << " beggers:" << beggers.size() \
<< "(mem:" << beggersMem << ")"; \
- qDebug() << "--- ----------------- ---"; \
+ dbgKrita << "--- ----------------- ---"; \
} while(0)
#define DEBUG_ALLOC_CLONE(mem, totalMem) \
- qDebug() << "Alloc mem for clones:" << mem \
+ dbgKrita << "Alloc mem for clones:" << mem \
<< "\tMem usage:" << totalMem << "/" << m_memoryLimit
#define DEBUG_FREE_CLONE(freed, demanded) \
- qDebug() << "Freed mem for clones:" << freed \
+ dbgKrita << "Freed mem for clones:" << freed \
<< "/" << qAbs(demanded)
#else
#define DEBUG_CLONE_ACTION(td, numClones)
#define DEBUG_SIMPLE_ACTION(action)
#define RUNTIME_SANITY_CHECK(td)
#define DEBUG_TILE_STATISTICS()
#define DEBUG_LISTS(mem, beggers, beggersMem, donors, donorsMem)
#define DEBUG_ALLOC_CLONE(mem, totalMem)
#define DEBUG_FREE_CLONE(freed, demanded)
#endif
KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit)
: QThread()
{
m_shouldExitFlag = 0;
m_store = store;
m_timeout = MIN_TIMEOUT;
m_lastCycleHadWork = false;
m_lastPoolMemoryMetric = 0;
m_lastRealMemoryMetric = 0;
m_lastHistoricalMemoryMetric = 0;
if(memoryLimit >= 0) {
m_memoryLimit = memoryLimit;
}
else {
KisImageConfig config;
m_memoryLimit = MiB_TO_METRIC(config.poolLimit());
}
}
KisTileDataPooler::~KisTileDataPooler()
{
}
void KisTileDataPooler::kick()
{
m_semaphore.release();
}
void KisTileDataPooler::terminatePooler()
{
unsigned long exitTimeout = 100;
do {
m_shouldExitFlag = true;
kick();
} while(!wait(exitTimeout));
}
qint32 KisTileDataPooler::numClonesNeeded(KisTileData *td) const
{
RUNTIME_SANITY_CHECK(td);
qint32 numUsers = td->m_usersCount;
qint32 numPresentClones = td->m_clonesStack.size();
qint32 totalClones = qMin(numUsers - 1, MAX_NUM_CLONES);
return totalClones - numPresentClones;
}
void KisTileDataPooler::cloneTileData(KisTileData *td, qint32 numClones) const
{
if (numClones > 0) {
td->blockSwapping();
for (qint32 i = 0; i < numClones; i++) {
td->m_clonesStack.push(new KisTileData(*td, false));
}
td->unblockSwapping();
} else {
qint32 numUnnededClones = qAbs(numClones);
for (qint32 i = 0; i < numUnnededClones; i++) {
KisTileData *clone = 0;
bool result = td->m_clonesStack.pop(clone);
if(!result) break;
delete clone;
}
}
DEBUG_CLONE_ACTION(td, numClones);
}
void KisTileDataPooler::waitForWork()
{
bool success;
if (m_lastCycleHadWork)
success = m_semaphore.tryAcquire(1, m_timeout);
else {
m_semaphore.acquire();
success = true;
}
m_lastCycleHadWork = false;
if (success) {
m_timeout = MIN_TIMEOUT;
} else {
m_timeout *= TIMEOUT_FACTOR;
m_timeout = qMin(m_timeout, MAX_TIMEOUT);
}
}
void KisTileDataPooler::run()
{
if(!m_memoryLimit) return;
m_shouldExitFlag = false;
while (1) {
DEBUG_SIMPLE_ACTION("went to bed... Zzz...");
waitForWork();
if (m_shouldExitFlag)
break;
QThread::msleep(0);
DEBUG_SIMPLE_ACTION("cycle started");
KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration();
QList<KisTileData*> beggers;
QList<KisTileData*> donors;
qint32 memoryOccupied;
qint32 statRealMemory;
qint32 statHistoricalMemory;
getLists(iter, beggers, donors,
memoryOccupied,
statRealMemory,
statHistoricalMemory);
m_lastCycleHadWork =
processLists(beggers, donors, memoryOccupied);
m_lastPoolMemoryMetric = memoryOccupied;
m_lastRealMemoryMetric = statRealMemory;
m_lastHistoricalMemoryMetric = statHistoricalMemory;
m_store->endIteration(iter);
DEBUG_TILE_STATISTICS();
DEBUG_SIMPLE_ACTION("cycle finished");
}
}
qint64 KisTileDataPooler::lastPoolMemoryMetric() const
{
return m_lastPoolMemoryMetric;
}
qint64 KisTileDataPooler::lastRealMemoryMetric() const
{
return m_lastRealMemoryMetric;
}
qint64 KisTileDataPooler::lastHistoricalMemoryMetric() const
{
return m_lastHistoricalMemoryMetric;
}
inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
return numClones * td->pixelSize();
}
inline int KisTileDataPooler::clonesMetric(KisTileData *td) {
return td->m_clonesStack.size() * td->pixelSize();
}
inline void KisTileDataPooler::tryFreeOrphanedClones(KisTileData *td)
{
qint32 extraClones = -numClonesNeeded(td);
if(extraClones > 0) {
cloneTileData(td, -extraClones);
}
}
inline qint32 KisTileDataPooler::needMemory(KisTileData *td)
{
qint32 clonesNeeded = !td->age() ? qMax(0, numClonesNeeded(td)) : 0;
return clonesMetric(td, clonesNeeded);
}
inline qint32 KisTileDataPooler::canDonorMemory(KisTileData *td)
{
return td->age() && clonesMetric(td);
}
template<class Iter>
void KisTileDataPooler::getLists(Iter *iter,
QList<KisTileData*> &beggers,
QList<KisTileData*> &donors,
qint32 &memoryOccupied,
qint32 &statRealMemory,
qint32 &statHistoricalMemory)
{
memoryOccupied = 0;
statRealMemory = 0;
statHistoricalMemory = 0;
qint32 needMemoryTotal = 0;
qint32 canDonorMemoryTotal = 0;
qint32 neededMemory;
qint32 donoredMemory;
KisTileData *item;
while(iter->hasNext()) {
item = iter->next();
tryFreeOrphanedClones(item);
if((neededMemory = needMemory(item))) {
needMemoryTotal += neededMemory;
beggers.append(item);
}
else if((donoredMemory = canDonorMemory(item))) {
canDonorMemoryTotal += donoredMemory;
donors.append(item);
}
memoryOccupied += clonesMetric(item);
// statistics gathering
if (item->historical()) {
statHistoricalMemory += item->pixelSize();
} else {
statRealMemory += item->pixelSize();
}
}
DEBUG_LISTS(memoryOccupied,
beggers, needMemoryTotal,
donors, canDonorMemoryTotal);
}
qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors,
qint32 memoryMetric)
{
qint32 memoryFreed = 0;
QMutableListIterator<KisTileData*> iter(donors);
iter.toBack();
while(iter.hasPrevious() && memoryFreed < memoryMetric) {
KisTileData *item = iter.previous();
qint32 numClones = item->m_clonesStack.size();
cloneTileData(item, -numClones);
memoryFreed += clonesMetric(item, numClones);
iter.remove();
}
return memoryFreed;
}
bool KisTileDataPooler::processLists(QList<KisTileData*> &beggers,
QList<KisTileData*> &donors,
qint32 &memoryOccupied)
{
bool hadWork = false;
foreach(KisTileData *item, beggers) {
qint32 clonesNeeded = numClonesNeeded(item);
qint32 clonesMemory = clonesMetric(item, clonesNeeded);
qint32 memoryLeft =
m_memoryLimit - (memoryOccupied + clonesMemory);
if(memoryLeft < 0) {
qint32 freedMemory = tryGetMemory(donors, -memoryLeft);
memoryOccupied -= freedMemory;
DEBUG_FREE_CLONE(freedMemory, memoryLeft);
if(m_memoryLimit < memoryOccupied + clonesMemory)
break;
}
cloneTileData(item, clonesNeeded);
DEBUG_ALLOC_CLONE(clonesMemory, memoryOccupied);
memoryOccupied += clonesMemory;
hadWork = true;
}
return hadWork;
}
void KisTileDataPooler::debugTileStatistics()
{
/**
* Assume we are called from the inside of the loop.
* This means m_store is already locked
*/
qint64 preallocatedTiles=0;
KisTileDataStoreIterator *iter = m_store->beginIteration();
KisTileData *item;
while(iter->hasNext()) {
item = iter->next();
preallocatedTiles += item->m_clonesStack.size();
}
m_store->endIteration(iter);
- qDebug() << "Tiles statistics:\t total:" << m_store->numTiles() << "\t preallocated:"<< preallocatedTiles;
+ dbgKrita << "Tiles statistics:\t total:" << m_store->numTiles() << "\t preallocated:"<< preallocatedTiles;
}
void KisTileDataPooler::testingRereadConfig()
{
KisImageConfig config;
m_memoryLimit = MiB_TO_METRIC(config.poolLimit());
}
diff --git a/krita/image/tiles3/kis_tile_data_store.cc b/krita/image/tiles3/kis_tile_data_store.cc
index 53d5fcc9e50..8b14d2893e4 100644
--- a/krita/image/tiles3/kis_tile_data_store.cc
+++ b/krita/image/tiles3/kis_tile_data_store.cc
@@ -1,354 +1,354 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kglobal.h>
// to disable assert when the leak tracker is active
#include "config-memory-leak-tracker.h"
#include "kis_tile_data_store.h"
#include "kis_tile_data.h"
#include "kis_debug.h"
#include "kis_tile_data_store_iterators.h"
//#define DEBUG_PRECLONE
#ifdef DEBUG_PRECLONE
#include <stdio.h>
#define DEBUG_PRECLONE_ACTION(action, oldTD, newTD) \
printf("!!! %s:\t\t\t 0x%X -> 0x%X \t\t!!!\n", \
action, (quintptr)oldTD, (quintptr) newTD)
#define DEBUG_FREE_ACTION(td) \
printf("Tile data free'd \t(0x%X)\n", td)
#else
#define DEBUG_PRECLONE_ACTION(action, oldTD, newTD)
#define DEBUG_FREE_ACTION(td)
#endif
#ifdef DEBUG_HIT_MISS
qint64 __preclone_miss = 0;
qint64 __preclone_hit = 0;
qint64 __preclone_miss_user_count = 0;
qint64 __preclone_miss_age = 0;
#define DEBUG_COUNT_PRECLONE_HIT(td) __preclone_hit++
#define DEBUG_COUNT_PRECLONE_MISS(td) __preclone_miss++; __preclone_miss_user_count+=td->numUsers(); __preclone_miss_age+=td->age()
#define DEBUG_REPORT_PRECLONE_EFFICIENCY() \
- qDebug() << "Hits:" << __preclone_hit \
+ dbgKrita << "Hits:" << __preclone_hit \
<< "of" << __preclone_hit + __preclone_miss \
<< "(" \
<< qreal(__preclone_hit) / (__preclone_hit + __preclone_miss) \
<< ")" \
<< "miss users" << qreal(__preclone_miss_user_count) / __preclone_miss \
<< "miss age" << qreal(__preclone_miss_age) / __preclone_miss
#else
#define DEBUG_COUNT_PRECLONE_HIT(td)
#define DEBUG_COUNT_PRECLONE_MISS(td)
#define DEBUG_REPORT_PRECLONE_EFFICIENCY()
#endif
KisTileDataStore::KisTileDataStore()
: m_pooler(this),
m_swapper(this),
m_numTiles(0),
m_memoryMetric(0)
{
m_clockIterator = m_tileDataList.end();
m_pooler.start();
m_swapper.start();
}
KisTileDataStore::~KisTileDataStore()
{
m_pooler.terminatePooler();
m_swapper.terminateSwapper();
if(numTiles() > 0) {
- qCritical() << "CRITICAL: According to statistics of the KisTileDataStore"
+ errKrita << "CRITICAL: According to statistics of the KisTileDataStore"
<< "some tiles have leaked from the Krita control!";
- qCritical() << "CRITICAL: Tiles in memory:" << numTilesInMemory()
+ errKrita << "CRITICAL: Tiles in memory:" << numTilesInMemory()
<< "Total tiles:" << numTiles();
}
}
KisTileDataStore* KisTileDataStore::instance()
{
K_GLOBAL_STATIC(KisTileDataStore, s_instance);
return s_instance;
}
KisTileDataStore::MemoryStatistics KisTileDataStore::memoryStatistics()
{
QMutexLocker lock(&m_listLock);
MemoryStatistics stats;
const qint64 metricCoeff = KisTileData::WIDTH * KisTileData::HEIGHT;
stats.realMemorySize = m_pooler.lastRealMemoryMetric() * metricCoeff;
stats.historicalMemorySize = m_pooler.lastHistoricalMemoryMetric() * metricCoeff;
stats.poolSize = m_pooler.lastPoolMemoryMetric() * metricCoeff;
stats.totalMemorySize = memoryMetric() * metricCoeff + stats.poolSize;
stats.swapSize = m_swappedStore.totalMemoryMetric() * metricCoeff;
return stats;
}
inline void KisTileDataStore::registerTileDataImp(KisTileData *td)
{
td->m_listIterator = m_tileDataList.insert(m_tileDataList.end(), td);
m_numTiles++;
m_memoryMetric += td->pixelSize();
}
void KisTileDataStore::registerTileData(KisTileData *td)
{
QMutexLocker lock(&m_listLock);
registerTileDataImp(td);
}
inline void KisTileDataStore::unregisterTileDataImp(KisTileData *td)
{
KisTileDataListIterator tempIterator = td->m_listIterator;
if(m_clockIterator == tempIterator) {
m_clockIterator = tempIterator + 1;
}
td->m_listIterator = m_tileDataList.end();
m_tileDataList.erase(tempIterator);
m_numTiles--;
m_memoryMetric -= td->pixelSize();
}
void KisTileDataStore::unregisterTileData(KisTileData *td)
{
QMutexLocker lock(&m_listLock);
unregisterTileDataImp(td);
}
KisTileData *KisTileDataStore::allocTileData(qint32 pixelSize, const quint8 *defPixel)
{
KisTileData *td = new KisTileData(pixelSize, defPixel, this);
registerTileData(td);
return td;
}
KisTileData *KisTileDataStore::duplicateTileData(KisTileData *rhs)
{
KisTileData *td = 0;
if (rhs->m_clonesStack.pop(td)) {
DEBUG_PRECLONE_ACTION("+ Pre-clone HIT", rhs, td);
DEBUG_COUNT_PRECLONE_HIT(rhs);
} else {
rhs->blockSwapping();
td = new KisTileData(*rhs);
rhs->unblockSwapping();
DEBUG_PRECLONE_ACTION("- Pre-clone #MISS#", rhs, td);
DEBUG_COUNT_PRECLONE_MISS(rhs);
}
registerTileData(td);
return td;
}
void KisTileDataStore::freeTileData(KisTileData *td)
{
Q_ASSERT(td->m_store == this);
DEBUG_FREE_ACTION(td);
m_listLock.lock();
td->m_swapLock.lockForWrite();
if(!td->data()) {
m_swappedStore.forgetTileData(td);
}
else {
unregisterTileDataImp(td);
}
td->m_swapLock.unlock();
m_listLock.unlock();
delete td;
}
void KisTileDataStore::ensureTileDataLoaded(KisTileData *td)
{
-// qDebug() << "#### SWAP MISS! ####" << td << ppVar(td->mementoed()) << ppVar(td->age()) << ppVar(td->numUsers());
+// dbgKrita << "#### SWAP MISS! ####" << td << ppVar(td->mementoed()) << ppVar(td->age()) << ppVar(td->numUsers());
checkFreeMemory();
td->m_swapLock.lockForRead();
while(!td->data()) {
td->m_swapLock.unlock();
/**
* The order of this heavy locking is very important.
* Change it only in case, you really know what you are doing.
*/
m_listLock.lock();
/**
* If someone has managed to load the td from swap, then, most
* probably, they have already taken the swap lock. This may
* lead to a deadlock, because COW mechanism breaks lock
* ordering rules in duplicateTileData() (it takes m_listLock
* while the swap lock is held). In our case it is enough just
* to check whether the other thread has already fetched the
* data. Please notice that we do not take both of the locks
* while checking this, because holding m_listLock is
* enough. Nothing can happen to the tile while we hold
* m_listLock.
*/
if(!td->data()) {
td->m_swapLock.lockForWrite();
m_swappedStore.swapInTileData(td);
registerTileDataImp(td);
td->m_swapLock.unlock();
}
m_listLock.unlock();
/**
* <-- In theory, livelock is possible here...
*/
td->m_swapLock.lockForRead();
}
}
bool KisTileDataStore::trySwapTileData(KisTileData *td)
{
/**
* This function is called with m_listLock acquired
*/
bool result = false;
if(!td->m_swapLock.tryLockForWrite()) return result;
if(td->data()) {
unregisterTileDataImp(td);
m_swappedStore.swapOutTileData(td);
result = true;
}
td->m_swapLock.unlock();
return result;
}
KisTileDataStoreIterator* KisTileDataStore::beginIteration()
{
m_listLock.lock();
return new KisTileDataStoreIterator(m_tileDataList, this);
}
void KisTileDataStore::endIteration(KisTileDataStoreIterator* iterator)
{
delete iterator;
m_listLock.unlock();
}
KisTileDataStoreReverseIterator* KisTileDataStore::beginReverseIteration()
{
m_listLock.lock();
return new KisTileDataStoreReverseIterator(m_tileDataList, this);
}
void KisTileDataStore::endIteration(KisTileDataStoreReverseIterator* iterator)
{
delete iterator;
m_listLock.unlock();
DEBUG_REPORT_PRECLONE_EFFICIENCY();
}
KisTileDataStoreClockIterator* KisTileDataStore::beginClockIteration()
{
m_listLock.lock();
return new KisTileDataStoreClockIterator(m_clockIterator, m_tileDataList, this);
}
void KisTileDataStore::endIteration(KisTileDataStoreClockIterator* iterator)
{
m_clockIterator = iterator->getFinalPosition();
delete iterator;
m_listLock.unlock();
}
void KisTileDataStore::debugPrintList()
{
KisTileData *item;
foreach(item, m_tileDataList) {
dbgTiles << "-------------------------\n"
<< "TileData:\t\t\t" << item
<< "\n refCount:\t" << item->m_refCount;
}
}
void KisTileDataStore::debugSwapAll()
{
KisTileDataStoreIterator* iter = beginIteration();
KisTileData *item;
while(iter->hasNext()) {
item = iter->next();
iter->trySwapOut(item);
}
endIteration(iter);
-// qDebug() << "Number of tiles:" << numTiles();
-// qDebug() << "Tiles in memory:" << numTilesInMemory();
+// dbgKrita << "Number of tiles:" << numTiles();
+// dbgKrita << "Tiles in memory:" << numTilesInMemory();
// m_swappedStore.debugStatistics();
}
void KisTileDataStore::debugClear()
{
QMutexLocker lock(&m_listLock);
foreach(KisTileData *item, m_tileDataList) {
delete item;
}
m_tileDataList.clear();
m_clockIterator = m_tileDataList.end();
m_numTiles = 0;
m_memoryMetric = 0;
}
void KisTileDataStore::testingRereadConfig() {
m_pooler.testingRereadConfig();
m_swapper.testingRereadConfig();
kickPooler();
}
void KisTileDataStore::testingSuspendPooler()
{
m_pooler.terminatePooler();
}
void KisTileDataStore::testingResumePooler()
{
m_pooler.start();
}
diff --git a/krita/image/tiles3/kis_tile_hash_table_p.h b/krita/image/tiles3/kis_tile_hash_table_p.h
index 05a8e0d5ba8..063673b73b2 100644
--- a/krita/image/tiles3/kis_tile_hash_table_p.h
+++ b/krita/image/tiles3/kis_tile_hash_table_p.h
@@ -1,398 +1,398 @@
/*
* Copyright (c) 2004 C. Boemann <cbo@boemann.dk>
* (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QtGlobal>
#include "kis_debug.h"
#include "kis_global.h"
//#define SHARED_TILES_SANITY_CHECK
template<class T>
KisTileHashTableTraits<T>::KisTileHashTableTraits(KisMementoManager *mm)
: m_lock(QReadWriteLock::NonRecursive)
{
m_hashTable = new TileTypeSP [TABLE_SIZE];
Q_CHECK_PTR(m_hashTable);
m_numTiles = 0;
m_defaultTileData = 0;
m_mementoManager = mm;
}
template<class T>
KisTileHashTableTraits<T>::KisTileHashTableTraits(const KisTileHashTableTraits<T> &ht,
KisMementoManager *mm)
: m_lock(QReadWriteLock::NonRecursive)
{
QReadLocker locker(&ht.m_lock);
m_mementoManager = mm;
m_defaultTileData = 0;
setDefaultTileDataImp(ht.m_defaultTileData);
m_hashTable = new TileTypeSP [TABLE_SIZE];
Q_CHECK_PTR(m_hashTable);
TileTypeSP foreignTile;
TileType* nativeTile;
TileType* nativeTileHead;
for (qint32 i = 0; i < TABLE_SIZE; i++) {
nativeTileHead = 0;
foreignTile = ht.m_hashTable[i];
while (foreignTile) {
nativeTile = new TileType(*foreignTile, m_mementoManager);
nativeTile->setNext(nativeTileHead);
nativeTileHead = nativeTile;
foreignTile = foreignTile->next();
}
m_hashTable[i] = nativeTileHead;
}
m_numTiles = ht.m_numTiles;
}
template<class T>
KisTileHashTableTraits<T>::~KisTileHashTableTraits()
{
clear();
delete[] m_hashTable;
setDefaultTileDataImp(0);
}
template<class T>
quint32 KisTileHashTableTraits<T>::calculateHash(qint32 col, qint32 row)
{
return ((row << 5) + (col & 0x1F)) & 0x3FF;
}
template<class T>
typename KisTileHashTableTraits<T>::TileTypeSP
KisTileHashTableTraits<T>::getTile(qint32 col, qint32 row)
{
qint32 idx = calculateHash(col, row);
TileTypeSP tile = m_hashTable[idx];
for (; tile; tile = tile->next()) {
if (tile->col() == col &&
tile->row() == row) {
return tile;
}
}
return 0;
}
template<class T>
void KisTileHashTableTraits<T>::linkTile(TileTypeSP tile)
{
qint32 idx = calculateHash(tile->col(), tile->row());
TileTypeSP firstTile = m_hashTable[idx];
#ifdef SHARED_TILES_SANITY_CHECK
Q_ASSERT_X(!tile->next(), "KisTileHashTableTraits<T>::linkTile",
"A tile can't be shared by several hash tables, sorry.");
#endif
tile->setNext(firstTile);
m_hashTable[idx] = tile;
m_numTiles++;
}
template<class T>
typename KisTileHashTableTraits<T>::TileTypeSP
KisTileHashTableTraits<T>::unlinkTile(qint32 col, qint32 row)
{
qint32 idx = calculateHash(col, row);
TileTypeSP tile = m_hashTable[idx];
TileTypeSP prevTile = 0;
for (; tile; tile = tile->next()) {
if (tile->col() == col &&
tile->row() == row) {
if (prevTile)
prevTile->setNext(tile->next());
else
/* optimize here*/
m_hashTable[idx] = tile->next();
/**
* The shared pointer may still be accessed by someone, so
* we need to disconnects the tile from memento manager
* explicitly
*/
tile->setNext(0);
tile->notifyDead();
tile = 0;
m_numTiles--;
return tile;
}
prevTile = tile;
}
return 0;
}
template<class T>
inline void KisTileHashTableTraits<T>::setDefaultTileDataImp(KisTileData *defaultTileData)
{
if (m_defaultTileData) {
m_defaultTileData->unblockSwapping();
m_defaultTileData->release();
m_defaultTileData = 0;
}
if (defaultTileData) {
defaultTileData->acquire();
defaultTileData->blockSwapping();
m_defaultTileData = defaultTileData;
}
}
template<class T>
inline KisTileData* KisTileHashTableTraits<T>::defaultTileDataImp() const
{
return m_defaultTileData;
}
template<class T>
bool KisTileHashTableTraits<T>::tileExists(qint32 col, qint32 row)
{
QReadLocker locker(&m_lock);
return getTile(col, row);
}
template<class T>
typename KisTileHashTableTraits<T>::TileTypeSP
KisTileHashTableTraits<T>::getExistedTile(qint32 col, qint32 row)
{
QReadLocker locker(&m_lock);
return getTile(col, row);
}
template<class T>
typename KisTileHashTableTraits<T>::TileTypeSP
KisTileHashTableTraits<T>::getTileLazy(qint32 col, qint32 row,
bool& newTile)
{
/**
* FIXME: Read access is better
*/
QWriteLocker locker(&m_lock);
newTile = false;
TileTypeSP tile = getTile(col, row);
if (!tile) {
tile = new TileType(col, row, m_defaultTileData, m_mementoManager);
linkTile(tile);
newTile = true;
}
return tile;
}
template<class T>
typename KisTileHashTableTraits<T>::TileTypeSP
KisTileHashTableTraits<T>::getReadOnlyTileLazy(qint32 col, qint32 row)
{
QReadLocker locker(&m_lock);
TileTypeSP tile = getTile(col, row);
if (!tile)
tile = new TileType(col, row, m_defaultTileData, 0);
return tile;
}
template<class T>
void KisTileHashTableTraits<T>::addTile(TileTypeSP tile)
{
QWriteLocker locker(&m_lock);
linkTile(tile);
}
template<class T>
void KisTileHashTableTraits<T>::deleteTile(qint32 col, qint32 row)
{
QWriteLocker locker(&m_lock);
TileTypeSP tile = unlinkTile(col, row);
/* Done by KisSharedPtr */
//if(tile)
// delete tile;
}
template<class T>
void KisTileHashTableTraits<T>::deleteTile(TileTypeSP tile)
{
deleteTile(tile->col(), tile->row());
}
template<class T>
void KisTileHashTableTraits<T>::clear()
{
QWriteLocker locker(&m_lock);
TileTypeSP tile = 0;
qint32 i;
for (i = 0; i < TABLE_SIZE; i++) {
tile = m_hashTable[i];
while (tile) {
TileTypeSP tmp = tile;
tile = tile->next();
/**
* About disconnection of tiles see a comment in unlinkTile()
*/
tmp->setNext(0);
tmp->notifyDead();
tmp = 0;
m_numTiles--;
}
m_hashTable[i] = 0;
}
Q_ASSERT(!m_numTiles);
}
template<class T>
void KisTileHashTableTraits<T>::setDefaultTileData(KisTileData *defaultTileData)
{
QWriteLocker locker(&m_lock);
setDefaultTileDataImp(defaultTileData);
}
template<class T>
KisTileData* KisTileHashTableTraits<T>::defaultTileData() const
{
QWriteLocker locker(&m_lock);
return defaultTileDataImp();
}
/*************** Debugging stuff ***************/
template<class T>
void KisTileHashTableTraits<T>::debugPrintInfo()
{
dbgTiles << "==========================\n"
<< "TileHashTable:"
<< "\n def. data:\t\t" << m_defaultTileData
<< "\n numTiles:\t\t" << m_numTiles;
debugListLengthDistibution();
dbgTiles << "==========================\n";
}
template<class T>
qint32 KisTileHashTableTraits<T>::debugChainLen(qint32 idx)
{
qint32 len = 0;
for (TileTypeSP it = m_hashTable[idx]; it; it = it->next(), len++) ;
return len;
}
template<class T>
void KisTileHashTableTraits<T>::debugMaxListLength(qint32 &min, qint32 &max)
{
TileTypeSP tile;
qint32 maxLen = 0;
qint32 minLen = m_numTiles;
qint32 tmp = 0;
for (qint32 i = 0; i < TABLE_SIZE; i++) {
tmp = debugChainLen(i);
if (tmp > maxLen)
maxLen = tmp;
if (tmp < minLen)
minLen = tmp;
}
min = minLen;
max = maxLen;
}
template<class T>
void KisTileHashTableTraits<T>::debugListLengthDistibution()
{
qint32 min, max;
qint32 arraySize;
qint32 tmp;
debugMaxListLength(min, max);
arraySize = max - min + 1;
qint32 *array = new qint32[arraySize];
memset(array, 0, sizeof(qint32)*arraySize);
for (qint32 i = 0; i < TABLE_SIZE; i++) {
tmp = debugChainLen(i);
array[tmp-min]++;
}
dbgTiles << QString(" minChain:\t\t%d\n"
" maxChain:\t\t%d").arg(min, max);
dbgTiles << " Chain size distribution:";
for (qint32 i = 0; i < arraySize; i++)
dbgTiles << QString(" %1:\t%2\n").arg(i + min, array[i]);
delete[] array;
}
template<class T>
void KisTileHashTableTraits<T>::sanityChecksumCheck()
{
/**
* We assume that the lock should have already been taken
* by the code that was going to change the table
*/
Q_ASSERT(!m_lock.tryLockForWrite());
TileTypeSP tile = 0;
qint32 exactNumTiles = 0;
for (qint32 i = 0; i < TABLE_SIZE; i++) {
tile = m_hashTable[i];
while (tile) {
exactNumTiles++;
tile = tile->next();
}
}
if (exactNumTiles != m_numTiles) {
- qDebug() << "Sanity check failed!";
- qDebug() << ppVar(exactNumTiles);
- qDebug() << ppVar(m_numTiles);
- qDebug() << "Wrong tiles checksum!";
- Q_ASSERT(0); // not qFatal() for a backtrace support
+ dbgKrita << "Sanity check failed!";
+ dbgKrita << ppVar(exactNumTiles);
+ dbgKrita << ppVar(m_numTiles);
+ dbgKrita << "Wrong tiles checksum!";
+ Q_ASSERT(0); // not fatalKrita for a backtrace support
}
}
diff --git a/krita/image/tiles3/swap/kis_chunk_allocator.cpp b/krita/image/tiles3/swap/kis_chunk_allocator.cpp
index 4ac8040b8e6..26998335de1 100644
--- a/krita/image/tiles3/swap/kis_chunk_allocator.cpp
+++ b/krita/image/tiles3/swap/kis_chunk_allocator.cpp
@@ -1,213 +1,213 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_debug.h"
#include "kis_chunk_allocator.h"
#define GAP_SIZE(low, high) ((high) - (low) > 0 ? (high) - (low) - 1 : 0)
#define HAS_NEXT(list,iter) ((iter)!=(list).end())
#define HAS_PREVIOUS(list,iter) ((iter)!=(list).begin())
#define PEEK_NEXT(iter) (*(iter))
#define PEEK_PREVIOUS(iter) (*((iter)-1))
#define WRAP_PREVIOUS_CHUNK_DATA(iter) (KisChunk((iter)-1))
KisChunkAllocator::KisChunkAllocator(quint64 slabSize, quint64 storeSize)
{
m_storeMaxSize = storeSize;
m_storeSlabSize = slabSize;
m_iterator = m_list.begin();
m_storeSize = m_storeSlabSize;
INIT_FAIL_COUNTER();
}
KisChunkAllocator::~KisChunkAllocator()
{
}
KisChunk KisChunkAllocator::getChunk(quint64 size)
{
KisChunkDataListIterator startPosition = m_iterator;
START_COUNTING();
forever {
if(tryInsertChunk(m_list, m_iterator, size))
return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
if(m_iterator == m_list.end())
break;
m_iterator++;
REGISTER_STEP();
}
REGISTER_FAIL();
m_iterator = m_list.begin();
forever {
if(tryInsertChunk(m_list, m_iterator, size))
return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
if(m_iterator == m_list.end() || m_iterator == startPosition)
break;
m_iterator++;
REGISTER_STEP();
}
REGISTER_FAIL();
m_iterator = m_list.end();
while ((m_storeSize += m_storeSlabSize) <= m_storeMaxSize) {
if(tryInsertChunk(m_list, m_iterator, size))
return WRAP_PREVIOUS_CHUNK_DATA(m_iterator);
}
qFatal("KisChunkAllocator: out of swap space");
// just let gcc be happy! :)
return KisChunk(m_list.end());
}
bool KisChunkAllocator::tryInsertChunk(KisChunkDataList &list,
KisChunkDataListIterator &iterator,
quint64 size)
{
bool result = false;
quint64 highBound = m_storeSize;
quint64 lowBound = 0;
quint64 shift = 0;
if(HAS_NEXT(list, iterator))
highBound = PEEK_NEXT(iterator).m_begin;
if(HAS_PREVIOUS(list, iterator)) {
lowBound = PEEK_PREVIOUS(iterator).m_end;
shift = 1;
}
if(GAP_SIZE(lowBound, highBound) >= size) {
list.insert(iterator, KisChunkData(lowBound + shift, size));
result = true;
}
return result;
}
void KisChunkAllocator::freeChunk(KisChunk chunk)
{
if(m_iterator != m_list.end() && m_iterator == chunk.position()) {
m_iterator = m_list.erase(m_iterator);
return;
}
Q_ASSERT(chunk.position()->m_begin == chunk.begin());
m_list.erase(chunk.position());
}
/**************************************************************/
/******* Debugging features ********/
/**************************************************************/
void KisChunkAllocator::debugChunks()
{
quint64 idx = 0;
KisChunkDataListIterator i;
for(i = m_list.begin(); i != m_list.end(); ++i) {
qDebug("chunk #%lld: [%lld %lld]", idx++, i->m_begin, i->m_end);
}
}
bool KisChunkAllocator::sanityCheck(bool pleaseCrash)
{
bool failed = false;
KisChunkDataListIterator i;
for(i = m_list.begin(); i != m_list.end(); ++i) {
if(HAS_PREVIOUS(m_list, i)) {
if(PEEK_PREVIOUS(i).m_end >= i->m_begin) {
qWarning("Chunks overlapped: [%lld %lld], [%lld %lld]", PEEK_PREVIOUS(i).m_begin, PEEK_PREVIOUS(i).m_end, i->m_begin, i->m_end);
failed = true;
break;
}
}
}
i = m_list.end();
if(HAS_PREVIOUS(m_list, i)) {
if(PEEK_PREVIOUS(i).m_end >= m_storeSize) {
- qWarning() << "Last chunk exceeds the store size!";
+ warnKrita << "Last chunk exceeds the store size!";
failed = true;
}
}
if(failed && pleaseCrash)
qFatal("KisChunkAllocator: sanity check failed!");
return !failed;
}
qreal KisChunkAllocator::debugFragmentation(bool toStderr)
{
KisChunkDataListIterator i;
quint64 totalSize = 0;
quint64 allocated = 0;
quint64 free = 0;
qreal fragmentation = 0;
for(i = m_list.begin(); i != m_list.end(); ++i) {
allocated += i->m_end - i->m_begin + 1;
if(HAS_PREVIOUS(m_list, i))
free += GAP_SIZE(PEEK_PREVIOUS(i).m_end, i->m_begin);
else
free += i->m_begin;
}
i = m_list.end();
if(HAS_PREVIOUS(m_list, i))
totalSize = PEEK_PREVIOUS(i).m_end + 1;
if(totalSize)
fragmentation = qreal(free) / totalSize;
if(toStderr) {
- qDebug() << "Hard store limit:\t" << m_storeMaxSize;
- qDebug() << "Slab size:\t\t" << m_storeSlabSize;
- qDebug() << "Num slabs:\t\t" << m_storeSize / m_storeSlabSize;
- qDebug() << "Store size:\t\t" << m_storeSize;
- qDebug() << "Total used:\t\t" << totalSize;
- qDebug() << "Allocated:\t\t" << allocated;
- qDebug() << "Free:\t\t\t" << free;
- qDebug() << "Fragmentation:\t\t" << fragmentation;
+ dbgKrita << "Hard store limit:\t" << m_storeMaxSize;
+ dbgKrita << "Slab size:\t\t" << m_storeSlabSize;
+ dbgKrita << "Num slabs:\t\t" << m_storeSize / m_storeSlabSize;
+ dbgKrita << "Store size:\t\t" << m_storeSize;
+ dbgKrita << "Total used:\t\t" << totalSize;
+ dbgKrita << "Allocated:\t\t" << allocated;
+ dbgKrita << "Free:\t\t\t" << free;
+ dbgKrita << "Fragmentation:\t\t" << fragmentation;
DEBUG_FAIL_COUNTER();
}
Q_ASSERT(totalSize == allocated + free);
return fragmentation;
}
diff --git a/krita/image/tiles3/swap/kis_chunk_allocator.h b/krita/image/tiles3/swap/kis_chunk_allocator.h
index 084d61acd93..b358f901068 100644
--- a/krita/image/tiles3/swap/kis_chunk_allocator.h
+++ b/krita/image/tiles3/swap/kis_chunk_allocator.h
@@ -1,164 +1,164 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_CHUNK_LIST_H
#define __KIS_CHUNK_LIST_H
#include "kritaimage_export.h"
#include <QLinkedList>
#define MiB (1ULL << 20)
#define DEFAULT_STORE_SIZE (4096*MiB)
#define DEFAULT_SLAB_SIZE (64*MiB)
//#define DEBUG_SLAB_FAILS
#ifdef DEBUG_SLAB_FAILS
#define WINDOW_SIZE 2000
#define DECLARE_FAIL_COUNTER() quint64 __failCount
#define INIT_FAIL_COUNTER() __failCount = 0
#define START_COUNTING() quint64 __numSteps = 0
#define REGISTER_STEP() if(++__numSteps > WINDOW_SIZE) {__numSteps=0; __failCount++;}
#define REGISTER_FAIL() __failCount++
-#define DEBUG_FAIL_COUNTER() qDebug() << "Slab fail count:\t" << __failCount
+#define DEBUG_FAIL_COUNTER() dbgKrita << "Slab fail count:\t" << __failCount
#else
#define DECLARE_FAIL_COUNTER()
#define INIT_FAIL_COUNTER()
#define START_COUNTING()
#define REGISTER_STEP()
#define REGISTER_FAIL()
#define DEBUG_FAIL_COUNTER()
#endif /* DEBUG_SLAB_FAILS */
class KisChunkData;
typedef QLinkedList<KisChunkData> KisChunkDataList;
typedef KisChunkDataList::iterator KisChunkDataListIterator;
class KisChunkData
{
public:
KisChunkData(quint64 begin, quint64 size)
{
setChunk(begin, size);
}
inline void setChunk(quint64 begin, quint64 size) {
m_begin = begin;
m_end = begin + size - 1;
}
inline quint64 size() const {
return m_end - m_begin +1;
}
bool operator== (const KisChunkData& other) const
{
Q_ASSERT(m_begin!=other.m_begin || m_end==other.m_end);
/**
* Chunks cannot overlap, so it is enough to check
* the beginning of the interval only
*/
return m_begin == other.m_begin;
}
quint64 m_begin;
quint64 m_end;
};
class KisChunk
{
public:
KisChunk() {}
KisChunk(KisChunkDataListIterator iterator)
: m_iterator(iterator)
{
}
inline quint64 begin() const {
return m_iterator->m_begin;
}
inline quint64 end() const {
return m_iterator->m_end;
}
inline quint64 size() const {
return m_iterator->size();
}
inline KisChunkDataListIterator position() {
return m_iterator;
}
inline const KisChunkData& data() {
return *m_iterator;
}
private:
KisChunkDataListIterator m_iterator;
};
class KRITAIMAGE_EXPORT KisChunkAllocator
{
public:
KisChunkAllocator(quint64 slabSize = DEFAULT_SLAB_SIZE,
quint64 storeSize = DEFAULT_STORE_SIZE);
~KisChunkAllocator();
inline quint64 numChunks() const {
return m_list.size();
}
KisChunk getChunk(quint64 size);
void freeChunk(KisChunk chunk);
void debugChunks();
bool sanityCheck(bool pleaseCrash = true);
qreal debugFragmentation(bool toStderr = true);
private:
bool tryInsertChunk(KisChunkDataList &list,
KisChunkDataListIterator &iterator,
quint64 size);
private:
quint64 m_storeMaxSize;
quint64 m_storeSlabSize;
KisChunkDataList m_list;
KisChunkDataListIterator m_iterator;
quint64 m_storeSize;
DECLARE_FAIL_COUNTER()
};
#endif /* __KIS_CHUNK_ALLOCATOR_H */
diff --git a/krita/image/tiles3/swap/kis_memory_window.cpp b/krita/image/tiles3/swap/kis_memory_window.cpp
index 77420a644e7..6a48f2fc6cf 100644
--- a/krita/image/tiles3/swap/kis_memory_window.cpp
+++ b/krita/image/tiles3/swap/kis_memory_window.cpp
@@ -1,121 +1,121 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_debug.h"
#include "kis_memory_window.h"
#include <QDir>
#define SWP_PREFIX "KRITA_SWAP_FILE_XXXXXX"
KisMemoryWindow::KisMemoryWindow(const QString &swapDir, quint64 writeWindowSize)
: m_readWindowEx(writeWindowSize / 4),
m_writeWindowEx(writeWindowSize)
{
QString swapFileTemplate = (swapDir.isEmpty() ? QDir::tempPath() : swapDir) + QDir::separator() + SWP_PREFIX;
QDir d(swapDir.isEmpty() ? QDir::tempPath() : swapDir);
if (!d.exists()) {
d.mkpath(swapDir.isEmpty() ? QDir::tempPath() : swapDir);
}
m_file.setFileTemplate(swapFileTemplate);
bool res = m_file.open();
Q_ASSERT(res);
Q_ASSERT(!m_file.fileName().isEmpty());
}
KisMemoryWindow::~KisMemoryWindow()
{
}
quint8* KisMemoryWindow::getReadChunkPtr(const KisChunkData &readChunk)
{
adjustWindow(readChunk, &m_readWindowEx, &m_writeWindowEx);
return m_readWindowEx.calculatePointer(readChunk);
}
quint8* KisMemoryWindow::getWriteChunkPtr(const KisChunkData &writeChunk)
{
adjustWindow(writeChunk, &m_writeWindowEx, &m_readWindowEx);
return m_writeWindowEx.calculatePointer(writeChunk);
}
void KisMemoryWindow::adjustWindow(const KisChunkData &requestedChunk,
MappingWindow *adjustingWindow,
MappingWindow *otherWindow)
{
if(!(adjustingWindow->window) ||
!(requestedChunk.m_begin >= adjustingWindow->chunk.m_begin &&
requestedChunk.m_end <= adjustingWindow->chunk.m_end))
{
m_file.unmap(adjustingWindow->window);
quint64 windowSize = adjustingWindow->defaultSize;
if(requestedChunk.size() > windowSize) {
- qWarning() <<
+ warnKrita <<
"KisMemoryWindow: the requested chunk is too "
"big to fit into the mapping! "
"Adjusting mapping to avoid SIGSEGV...";
windowSize = requestedChunk.size();
}
adjustingWindow->chunk.setChunk(requestedChunk.m_begin, windowSize);
if(adjustingWindow->chunk.m_end >= (quint64)m_file.size()) {
// Align by 32 bytes
quint64 newSize = (adjustingWindow->chunk.m_end + 1 + 32) & (~31ULL);
#ifdef Q_OS_WIN32
/**
* Workaround for Qt's "feature"
*
* On windows QFSEnginePrivate caches the value of
* mapHandle which is limited to the size of the file at
* the moment of its (handle's) creation. That is we will
* not be able to use it after resizing the file. The
* only way to free the handle is to release all the
* mappings we have. Sad but true.
*/
if (otherWindow->chunk.size()) {
m_file.unmap(otherWindow->window);
}
#else
Q_UNUSED(otherWindow);
#endif
m_file.resize(newSize);
#ifdef Q_OS_WIN32
if (otherWindow->chunk.size()) {
otherWindow->window = m_file.map(otherWindow->chunk.m_begin,
otherWindow->chunk.size());
}
#endif
}
#ifdef Q_OS_UNIX
// A workaround for https://bugreports.qt-project.org/browse/QTBUG-6330
m_file.exists();
#endif
adjustingWindow->window = m_file.map(adjustingWindow->chunk.m_begin,
adjustingWindow->chunk.size());
}
}
diff --git a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
index 1dc4924095d..161a4008239 100644
--- a/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
+++ b/krita/image/tiles3/swap/kis_tile_data_swapper.cpp
@@ -1,242 +1,242 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QSemaphore>
#include "tiles3/swap/kis_tile_data_swapper.h"
#include "tiles3/swap/kis_tile_data_swapper_p.h"
#include "tiles3/kis_tile_data.h"
#include "tiles3/kis_tile_data_store.h"
#include "tiles3/kis_tile_data_store_iterators.h"
#include "kis_debug.h"
#define SEC 1000
const qint32 KisTileDataSwapper::TIMEOUT = -1;
const qint32 KisTileDataSwapper::DELAY = 0.7 * SEC;
//#define DEBUG_SWAPPER
#ifdef DEBUG_SWAPPER
-#define DEBUG_ACTION(action) qDebug() << action
-#define DEBUG_VALUE(value) qDebug() << "\t" << ppVar(value)
+#define DEBUG_ACTION(action) dbgKrita << action
+#define DEBUG_VALUE(value) dbgKrita << "\t" << ppVar(value)
#else
#define DEBUG_ACTION(action)
#define DEBUG_VALUE(value)
#endif
class SoftSwapStrategy;
class AggressiveSwapStrategy;
struct Q_DECL_HIDDEN KisTileDataSwapper::Private
{
public:
QSemaphore semaphore;
QAtomicInt shouldExitFlag;
KisTileDataStore *store;
KisStoreLimits limits;
QMutex cycleLock;
};
KisTileDataSwapper::KisTileDataSwapper(KisTileDataStore *store)
: QThread(),
m_d(new Private())
{
m_d->shouldExitFlag = 0;
m_d->store = store;
}
KisTileDataSwapper::~KisTileDataSwapper()
{
delete m_d;
}
void KisTileDataSwapper::kick()
{
m_d->semaphore.release();
}
void KisTileDataSwapper::terminateSwapper()
{
unsigned long exitTimeout = 100;
do {
m_d->shouldExitFlag = true;
kick();
} while(!wait(exitTimeout));
}
void KisTileDataSwapper::waitForWork()
{
m_d->semaphore.tryAcquire(1, TIMEOUT);
}
void KisTileDataSwapper::run()
{
while (1) {
waitForWork();
if (m_d->shouldExitFlag)
return;
QThread::msleep(DELAY);
doJob();
}
}
void KisTileDataSwapper::checkFreeMemory()
{
-// qDebug() <<"check memory: high limit -" << m_d->limits.emergencyThreshold() <<"in mem -" << m_d->store->numTilesInMemory();
+// dbgKrita <<"check memory: high limit -" << m_d->limits.emergencyThreshold() <<"in mem -" << m_d->store->numTilesInMemory();
if(m_d->store->memoryMetric() > m_d->limits.emergencyThreshold())
doJob();
}
void KisTileDataSwapper::doJob()
{
/**
* In emergency case usual threads have access
* to this function as well
*/
QMutexLocker locker(&m_d->cycleLock);
qint32 memoryMetric = m_d->store->memoryMetric();
DEBUG_ACTION("Started swap cycle");
DEBUG_VALUE(m_d->store->numTiles());
DEBUG_VALUE(m_d->store->numTilesInMemory());
DEBUG_VALUE(memoryMetric);
DEBUG_VALUE(m_d->limits.softLimitThreshold());
DEBUG_VALUE(m_d->limits.hardLimitThreshold());
if(memoryMetric > m_d->limits.softLimitThreshold()) {
qint32 softFree = memoryMetric - m_d->limits.softLimit();
DEBUG_VALUE(softFree);
DEBUG_ACTION("\t pass0");
memoryMetric -= pass<SoftSwapStrategy>(softFree);
DEBUG_VALUE(memoryMetric);
if(memoryMetric > m_d->limits.hardLimitThreshold()) {
qint32 hardFree = memoryMetric - m_d->limits.hardLimit();
DEBUG_VALUE(hardFree);
DEBUG_ACTION("\t pass1");
memoryMetric -= pass<AggressiveSwapStrategy>(hardFree);
DEBUG_VALUE(memoryMetric);
}
}
}
class SoftSwapStrategy
{
public:
typedef KisTileDataStoreIterator iterator;
static inline iterator* beginIteration(KisTileDataStore *store) {
return store->beginIteration();
}
static inline void endIteration(KisTileDataStore *store, iterator *iter) {
store->endIteration(iter);
}
static inline bool isInteresting(KisTileData *td) {
// We are working with mementoed tiles only...
return td->historical();
}
static inline bool swapOutFirst(KisTileData *td) {
return td->age() > 0;
}
};
class AggressiveSwapStrategy
{
public:
typedef KisTileDataStoreClockIterator iterator;
static inline iterator* beginIteration(KisTileDataStore *store) {
return store->beginClockIteration();
}
static inline void endIteration(KisTileDataStore *store, iterator *iter) {
store->endIteration(iter);
}
static inline bool isInteresting(KisTileData *td) {
// Add some aggression...
Q_UNUSED(td);
return true; // >:)
}
static inline bool swapOutFirst(KisTileData *td) {
return td->age() > 0;
}
};
template<class strategy>
qint64 KisTileDataSwapper::pass(qint64 needToFreeMetric)
{
qint64 freedMetric = 0;
QList<KisTileData*> additionalCandidates;
typename strategy::iterator *iter =
strategy::beginIteration(m_d->store);
KisTileData *item;
while(iter->hasNext()) {
item = iter->next();
if(freedMetric >= needToFreeMetric) break;
if(!strategy::isInteresting(item)) continue;
if(strategy::swapOutFirst(item)) {
if(iter->trySwapOut(item)) {
freedMetric += item->pixelSize();
}
}
else {
item->markOld();
additionalCandidates.append(item);
}
}
foreach(item, additionalCandidates) {
if(freedMetric >= needToFreeMetric) break;
if(iter->trySwapOut(item)) {
freedMetric += item->pixelSize();
}
}
strategy::endIteration(m_d->store, iter);
return freedMetric;
}
void KisTileDataSwapper::testingRereadConfig()
{
m_d->limits = KisStoreLimits();
}
diff --git a/krita/image/tiles3/tests/CMakeLists.txt b/krita/image/tiles3/tests/CMakeLists.txt
index d7c0eae3b2d..881b3ab977d 100644
--- a/krita/image/tiles3/tests/CMakeLists.txt
+++ b/krita/image/tiles3/tests/CMakeLists.txt
@@ -1,66 +1,66 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${KOGUIUTILS_INCLUDES} )
macro_add_unittest_definitions()
########### next target ###############
set(kis_tiled_data_manager_test_SRCS kis_tiled_data_manager_test.cpp )
kde4_add_unit_test(KisTiledDataManagerTest TESTNAME krita-image-KisTiledDataManagerTest ${kis_tiled_data_manager_test_SRCS})
target_link_libraries(KisTiledDataManagerTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
########### next target ###############
set(kis_low_memory_tests_SRCS kis_low_memory_tests.cpp )
kde4_add_unit_test(KisLowMemoryTests TESTNAME krita-image-KisLowMemoryTests ${kis_low_memory_tests_SRCS})
target_link_libraries(KisLowMemoryTests ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
set_tests_properties(krita-image-KisLowMemoryTests PROPERTIES TIMEOUT 180)
########### next target ###############
set(kis_tile_compressors_test_SRCS kis_tile_compressors_test.cpp )
kde4_add_unit_test(KisTileCompressorsTest TESTNAME krita-image-KisTileCompressorsTest ${kis_tile_compressors_test_SRCS})
target_link_libraries(KisTileCompressorsTest ${KDE4_KDEUI_LIBS} koodf kritaimage Qt5::Test)
########### next target ###############
set(kis_compression_tests_SRCS kis_compression_tests.cpp )
kde4_add_unit_test(KisCompressionTests TESTNAME krita-image-KisCompressionTests ${kis_compression_tests_SRCS})
target_link_libraries(KisCompressionTests ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
########### next target ###############
set(kis_lockless_stack_test_SRCS kis_lockless_stack_test.cpp )
kde4_add_unit_test(KisLocklessStackTest TESTNAME krita-image-KisLocklessStackTest ${kis_lockless_stack_test_SRCS})
target_link_libraries(KisLocklessStackTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test)
########### next target ###############
set(kis_memory_pool_test_SRCS kis_memory_pool_test.cpp )
kde4_add_unit_test(KisMemoryPoolTest TESTNAME krita-image-KisMemoryPoolTest ${kis_memory_pool_test_SRCS})
-target_link_libraries(KisMemoryPoolTest ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(KisMemoryPoolTest kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(kis_chunk_allocator_test_SRCS kis_chunk_allocator_test.cpp ../swap/kis_chunk_allocator.cpp)
kde4_add_unit_test(KisChunkAllocatorTest TESTNAME krita-image-KisChunkAllocatorTest ${kis_chunk_allocator_test_SRCS})
-target_link_libraries(KisChunkAllocatorTest ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(KisChunkAllocatorTest kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(kis_memory_window_test_SRCS kis_memory_window_test.cpp ../swap/kis_memory_window.cpp)
kde4_add_unit_test(KisMemoryWindowTest TESTNAME krita-image-KisMemoryWindowTest ${kis_memory_window_test_SRCS})
-target_link_libraries(KisMemoryWindowTest ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(KisMemoryWindowTest kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(kis_swapped_data_store_test_SRCS kis_swapped_data_store_test.cpp ../kis_tile_data.cc)
kde4_add_unit_test(KisSwappedDataStoreTest TESTNAME krita-image-KisSwappedDataStoreTest ${kis_swapped_data_store_test_SRCS})
target_link_libraries(KisSwappedDataStoreTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY})
########### next target ###############
set(kis_tile_data_store_test_SRCS kis_tile_data_store_test.cpp ../kis_tile_data.cc)
kde4_add_unit_test(KisTileDataStoreTest TESTNAME krita-image-KisTileDataStoreTest ${kis_tile_data_store_test_SRCS})
target_link_libraries(KisTileDataStoreTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY})
########### next target ###############
set(kis_store_limits_test_SRCS kis_store_limits_test.cpp ../kis_tile_data.cc )
kde4_add_unit_test(KisStoreLimitsTest TESTNAME krita-image-KisStoreLimitsTest ${kis_store_limits_test_SRCS})
target_link_libraries(KisStoreLimitsTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY})
########### next target ###############
set(kis_tile_data_pooler_test_SRCS kis_tile_data_pooler_test.cpp ../kis_tile_data.cc ../kis_tile_data_pooler.cc )
kde4_add_unit_test(KisTileDataPoolerTest TESTNAME krita-image-KisTileDataPoolerTest ${kis_tile_data_pooler_test_SRCS})
target_link_libraries(KisTileDataPoolerTest ${KDE4_KDEUI_LIBS} kritaimage Qt5::Test ${Boost_SYSTEM_LIBRARY})
diff --git a/krita/image/tiles3/tests/kis_chunk_allocator_test.cpp b/krita/image/tiles3/tests/kis_chunk_allocator_test.cpp
index c24ad7183f0..5bde30b5ed7 100644
--- a/krita/image/tiles3/tests/kis_chunk_allocator_test.cpp
+++ b/krita/image/tiles3/tests/kis_chunk_allocator_test.cpp
@@ -1,125 +1,125 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_chunk_allocator_test.h"
#include <qtest_kde.h>
#include "kis_debug.h"
#include "tiles3/swap/kis_chunk_allocator.h"
void KisChunkAllocatorTest::testOperations()
{
KisChunkAllocator allocator;
allocator.getChunk(10);
allocator.getChunk(15);
KisChunk chunk3 = allocator.getChunk(20);
allocator.getChunk(25);
allocator.getChunk(30);
allocator.freeChunk(chunk3);
chunk3 = allocator.getChunk(20);
allocator.debugChunks();
allocator.sanityCheck();
QVERIFY(qFuzzyCompare(allocator.debugFragmentation(), 1./6));
}
#define NUM_TRANSACTIONS 30
#define NUM_CHUNKS_ALLOC 15000
#define NUM_CHUNKS_FREE 12000
#define CHUNK_AV_SIZE 1024*12
#define CHUNK_DEV_SIZE 1024*4
#define SWAP_SIZE (1ULL * CHUNK_AV_SIZE * NUM_CHUNKS_ALLOC * NUM_TRANSACTIONS)
quint64 getChunkSize()
{
quint64 deviation = qrand() % (2 * CHUNK_DEV_SIZE);
return CHUNK_AV_SIZE - CHUNK_DEV_SIZE + deviation;
}
qreal KisChunkAllocatorTest::measureFragmentation(qint32 transactions,
qint32 chunksAlloc,
qint32 chunksFree,
bool printDetails)
{
KisChunkAllocator allocator(DEFAULT_SLAB_SIZE, SWAP_SIZE);
QList<KisChunk> chunks;
for(qint32 k = 0; k < transactions; k++) {
if(chunks.size() > 0) {
for(qint32 i = 0; i < chunksFree; i++) {
qint32 idx = qrand() % chunks.size();
allocator.freeChunk(chunks.takeAt(idx));
}
}
allocator.sanityCheck();
for(qint32 i = 0; i < chunksAlloc; i++) {
chunks.append(allocator.getChunk(getChunkSize()));
}
allocator.sanityCheck();
}
allocator.sanityCheck();
return allocator.debugFragmentation(printDetails);
}
void KisChunkAllocatorTest::testFragmentation()
{
qsrand(QTime::currentTime().msec());
measureFragmentation(NUM_TRANSACTIONS, NUM_CHUNKS_ALLOC,
NUM_CHUNKS_FREE, true);
/**
* The following tests are too slow, so we disable them by default
*/
return;
- qDebug() << "fragmentation(transactions)";
+ dbgKrita << "fragmentation(transactions)";
for(qint32 t = 1; t < NUM_TRANSACTIONS; t += NUM_TRANSACTIONS/7) {
qreal f = measureFragmentation(t, NUM_CHUNKS_ALLOC,
NUM_CHUNKS_FREE, false);
- qDebug() << t << f;
+ dbgKrita << t << f;
}
- qDebug() << "fragmentation(alloc)";
+ dbgKrita << "fragmentation(alloc)";
for(qint32 t = 1; t < NUM_CHUNKS_ALLOC; t += NUM_CHUNKS_ALLOC/7) {
qreal f = measureFragmentation(NUM_TRANSACTIONS,t,
0.8*t, false);
- qDebug() << t << f;
+ dbgKrita << t << f;
}
- qDebug() << "fragmentation(free)";
+ dbgKrita << "fragmentation(free)";
for(qint32 t = NUM_CHUNKS_ALLOC/7; t < NUM_CHUNKS_ALLOC; t += NUM_CHUNKS_ALLOC/15) {
qreal f = measureFragmentation(NUM_TRANSACTIONS,NUM_CHUNKS_ALLOC,
t, false);
- qDebug() << t << f;
+ dbgKrita << t << f;
}
}
QTEST_KDEMAIN(KisChunkAllocatorTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_compression_tests.cpp b/krita/image/tiles3/tests/kis_compression_tests.cpp
index ae161548af6..47b331c33c9 100644
--- a/krita/image/tiles3/tests/kis_compression_tests.cpp
+++ b/krita/image/tiles3/tests/kis_compression_tests.cpp
@@ -1,264 +1,266 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_compression_tests.h"
#include <qtest_kde.h>
#include <QImage>
#include "../../../sdk/tests/testutil.h"
#include "tiles3/swap/kis_lzf_compression.h"
-
+#include <kis_debug.h>
#define TEST_FILE "tile.png"
//#define TEST_FILE "hakonepa.png"
-#define PRINT_COMPRESSION(title,src,dst) \
- (qDebug() << title << dst << "/" << src << "\t|" << double(dst)/src)
+void PRINT_COMPRESSION(const QString &title, quint32 src, quint32 dst) {
+
+ dbgKrita << title << dst << "/" << src << "\t|" << double(dst)/src;
+}
void KisCompressionTests::roundTrip(KisAbstractCompression *compression)
{
QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
QImage image(referenceImage);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
qint32 uncompressedBytes;
compressedBytes = compression->compress(image.bits(), srcSize,
output, outputSize);
uncompressedBytes = compression->decompress(output, compressedBytes,
image.bits(), srcSize);
PRINT_COMPRESSION("Single-pass:\t", uncompressedBytes, compressedBytes);
QCOMPARE(uncompressedBytes, srcSize);
QVERIFY(referenceImage == image);
}
void KisCompressionTests::roundTripTwoPass(KisAbstractCompression *compression)
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
qint32 uncompressedBytes;
quint8 *tempBuffer = new quint8[srcSize];
KisAbstractCompression::linearizeColors(image.bits(), tempBuffer,
srcSize, 4);
compressedBytes = compression->compress(tempBuffer, srcSize,
output, outputSize);
uncompressedBytes = compression->decompress(output, compressedBytes,
tempBuffer, srcSize);
KisAbstractCompression::delinearizeColors(tempBuffer, image.bits(),
srcSize, 4);
PRINT_COMPRESSION("Two-pass:\t", uncompressedBytes, compressedBytes);
QCOMPARE(uncompressedBytes, srcSize);
QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
QVERIFY(referenceImage == image);
}
void KisCompressionTests::benchmarkCompression(KisAbstractCompression *compression)
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
QBENCHMARK {
compressedBytes = compression->compress(image.bits(), srcSize,
output, outputSize);
}
Q_UNUSED(compressedBytes);
}
void KisCompressionTests::benchmarkCompressionTwoPass(KisAbstractCompression *compression)
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
quint8 *tempBuffer = new quint8[srcSize];
QBENCHMARK {
KisAbstractCompression::linearizeColors(image.bits(), tempBuffer,
srcSize, 4);
compressedBytes = compression->compress(tempBuffer, srcSize,
output, outputSize);
}
Q_UNUSED(compressedBytes);
}
void KisCompressionTests::benchmarkDecompression(KisAbstractCompression *compression)
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
qint32 uncompressedBytes;
compressedBytes = compression->compress(image.bits(), srcSize,
output, outputSize);
QBENCHMARK {
uncompressedBytes = compression->decompress(output, compressedBytes,
image.bits(), srcSize);
}
Q_UNUSED(uncompressedBytes);
}
void KisCompressionTests::benchmarkDecompressionTwoPass(KisAbstractCompression *compression)
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
qint32 uncompressedBytes;
quint8 *tempBuffer = new quint8[srcSize];
KisAbstractCompression::linearizeColors(image.bits(), tempBuffer, srcSize, 4);
compressedBytes = compression->compress(tempBuffer, srcSize,
output, outputSize);
QBENCHMARK {
uncompressedBytes = compression->decompress(output, compressedBytes,
tempBuffer, srcSize);
KisAbstractCompression::delinearizeColors(tempBuffer, image.bits(),
srcSize, 4);
}
Q_UNUSED(uncompressedBytes);
}
void KisCompressionTests::testOverflow(KisAbstractCompression *compression)
{
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QByteArray array = file.readAll();
qint32 srcSize = array.count();
qint32 outputSize = compression->outputBufferSize(srcSize);
quint8 *output = new quint8[outputSize];
qint32 compressedBytes;
compressedBytes = compression->compress((quint8*)array.data(), srcSize,
output, outputSize);
PRINT_COMPRESSION("Uncompressable:\t", srcSize, compressedBytes);
- qDebug() << "Max buffer size:" << outputSize;
+ dbgKrita << "Max buffer size:" << outputSize;
QVERIFY(compressedBytes <= outputSize);
}
void KisCompressionTests::testLzfRoundTrip()
{
KisAbstractCompression *compression = new KisLzfCompression();
roundTrip(compression);
roundTripTwoPass(compression);
delete compression;
}
void KisCompressionTests::testLzfOverflow()
{
KisAbstractCompression *compression = new KisLzfCompression();
testOverflow(compression);
delete compression;
}
void KisCompressionTests::benchmarkMemCpy()
{
QImage image(QString(FILES_DATA_DIR) + QDir::separator() + TEST_FILE);
qint32 srcSize = image.byteCount();
quint8 *output = new quint8[srcSize];
QBENCHMARK {
memcpy(output, image.bits(), srcSize);
}
delete[] output;
}
void KisCompressionTests::benchmarkCompressionLzf()
{
KisAbstractCompression *compression = new KisLzfCompression();
benchmarkCompression(compression);
delete compression;
}
void KisCompressionTests::benchmarkCompressionLzfTwoPass()
{
KisAbstractCompression *compression = new KisLzfCompression();
benchmarkCompressionTwoPass(compression);
delete compression;
}
void KisCompressionTests::benchmarkDecompressionLzf()
{
KisAbstractCompression *compression = new KisLzfCompression();
benchmarkDecompression(compression);
delete compression;
}
void KisCompressionTests::benchmarkDecompressionLzfTwoPass()
{
KisAbstractCompression *compression = new KisLzfCompression();
benchmarkDecompressionTwoPass(compression);
delete compression;
}
QTEST_KDEMAIN(KisCompressionTests, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_lockless_stack_test.cpp b/krita/image/tiles3/tests/kis_lockless_stack_test.cpp
index 266c03b7289..d96d00f68ec 100644
--- a/krita/image/tiles3/tests/kis_lockless_stack_test.cpp
+++ b/krita/image/tiles3/tests/kis_lockless_stack_test.cpp
@@ -1,293 +1,293 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_lockless_stack_test.h"
#include <qtest_kde.h>
#include "kis_debug.h"
#include "tiles3/kis_lockless_stack.h"
void KisLocklessStackTest::testOperations()
{
KisLocklessStack<int> stack;
for(qint32 i = 0; i < 1024; i++) {
stack.push(i);
}
QCOMPARE(stack.size(), 1024);
for(qint32 i = 1023; i >= 0; i--) {
int value;
bool result = stack.pop(value);
QVERIFY(result);
QCOMPARE(value, i);
}
QVERIFY(stack.isEmpty());
}
/************ BENCHMARKING INFRASTRACTURE ************************/
#define NUM_TYPES 2
// high-concurrency
#define NUM_CYCLES 500000
#define NUM_THREADS 10
// relaxed
//#define NUM_CYCLES 100
//#define NUM_THREADS 2
// single-threaded
//#define NUM_CYCLES 10000000
//#define NUM_THREADS 1
class KisAbstractIntStack
{
public:
virtual ~KisAbstractIntStack() {}
virtual void push(int value) = 0;
virtual int pop() = 0;
virtual bool isEmpty() = 0;
virtual void clear() = 0;
};
class KisTestingLocklessStack : public KisAbstractIntStack
{
public:
void push(int value) {
m_stack.push(value);
}
int pop() {
int value = 0;
bool result = m_stack.pop(value);
Q_ASSERT(result);
Q_UNUSED(result); // for release build
return value;
}
bool isEmpty() {
return m_stack.isEmpty();
}
void clear() {
m_stack.clear();
}
private:
KisLocklessStack<int> m_stack;
};
class KisTestingLegacyStack : public KisAbstractIntStack
{
public:
void push(int value) {
m_mutex.lock();
m_stack.push(value);
m_mutex.unlock();
}
int pop() {
m_mutex.lock();
int result = m_stack.pop();
m_mutex.unlock();
return result;
}
bool isEmpty() {
m_mutex.lock();
bool result = m_stack.isEmpty();
m_mutex.unlock();
return result;
}
void clear() {
m_mutex.lock();
m_stack.clear();
m_mutex.unlock();
}
private:
QStack<int> m_stack;
QMutex m_mutex;
};
class KisStressJob : public QRunnable
{
public:
KisStressJob(KisAbstractIntStack &stack, qint32 startValue)
: m_stack(stack), m_startValue(startValue)
{
m_pushSum = 0;
m_popSum = 0;
}
void run() {
for(qint32 i = 0; i < NUM_CYCLES; i++) {
qint32 type = i % NUM_TYPES;
int newValue;
switch(type) {
case 0:
newValue = m_startValue + i;
m_pushSum += newValue;
m_stack.push(newValue);
break;
case 1:
m_popSum += m_stack.pop();
break;
}
}
}
qint64 pushSum() {
return m_pushSum;
}
qint64 popSum() {
return m_popSum;
}
private:
KisAbstractIntStack &m_stack;
qint32 m_startValue;
qint64 m_pushSum;
qint64 m_popSum;
};
void KisLocklessStackTest::runStressTest(KisAbstractIntStack &stack)
{
QList<KisStressJob*> jobsList;
KisStressJob *job;
for(qint32 i = 0; i < NUM_THREADS; i++) {
job = new KisStressJob(stack, 1);
job->setAutoDelete(false);
jobsList.append(job);
}
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
QBENCHMARK {
foreach(job, jobsList) {
pool.start(job);
}
pool.waitForDone();
}
QVERIFY(stack.isEmpty());
qint64 totalSum = 0;
for(qint32 i = 0; i < NUM_THREADS; i++) {
KisStressJob *job = jobsList.takeLast();
totalSum += job->pushSum();
totalSum -= job->popSum();
- qDebug() << ppVar(totalSum);
+ dbgKrita << ppVar(totalSum);
delete job;
}
QCOMPARE(totalSum, (long long) 0);
}
void KisLocklessStackTest::stressTestLockless()
{
KisTestingLocklessStack stack;
runStressTest(stack);
}
void KisLocklessStackTest::stressTestQStack()
{
KisTestingLegacyStack stack;
runStressTest(stack);
}
class KisStressClearJob : public QRunnable
{
public:
KisStressClearJob(KisLocklessStack<int> &stack, qint32 startValue)
: m_stack(stack), m_startValue(startValue)
{
}
void run() {
for(qint32 i = 0; i < NUM_CYCLES; i++) {
qint32 type = i % 4;
int newValue;
switch(type) {
case 0:
case 1:
newValue = m_startValue + i;
m_stack.push(newValue);
break;
case 2:
int tmp;
m_stack.pop(tmp);
break;
case 3:
m_stack.clear();
break;
}
}
}
private:
KisLocklessStack<int> &m_stack;
qint32 m_startValue;
};
void KisLocklessStackTest::stressTestClear()
{
KisLocklessStack<int> stack;
KisStressClearJob *job;
QThreadPool pool;
pool.setMaxThreadCount(NUM_THREADS);
for(qint32 i = 0; i < NUM_THREADS; i++) {
job = new KisStressClearJob(stack, 1);
pool.start(job);
}
pool.waitForDone();
stack.clear();
QVERIFY(stack.isEmpty());
}
QTEST_KDEMAIN(KisLocklessStackTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_low_memory_tests.cpp b/krita/image/tiles3/tests/kis_low_memory_tests.cpp
index 43dcb4b33c6..77bd09fc5c7 100644
--- a/krita/image/tiles3/tests/kis_low_memory_tests.cpp
+++ b/krita/image/tiles3/tests/kis_low_memory_tests.cpp
@@ -1,215 +1,215 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_low_memory_tests.h"
#include <qtest_kde.h>
#include <QThreadPool>
#include "kis_image_config.h"
#include "tiles_test_utils.h"
#include "tiles3/kis_tiled_data_manager.h"
#include "tiles3/kis_tile_data_store.h"
-
+#include <kis_debug.h>
void KisLowMemoryTests::initTestCase()
{
// hard limit of 1MiB, no undo in memory, no clones
KisImageConfig config;
config.setMemoryHardLimitPercent(1.1 * 100.0 / KisImageConfig::totalRAM());
config.setMemorySoftLimitPercent(0);
config.setMemoryPoolLimitPercent(0);
}
class DeadlockyThread : public QRunnable
{
public:
enum Type {
PRODUCER,
CONSUMER_SRC,
CONSUMER_DST
};
DeadlockyThread(Type type,
KisTiledDataManager &srcDM,
KisTiledDataManager &dstDM,
int numTiles,
int numCycles)
: m_type(type),
m_srcDM(srcDM),
m_dstDM(dstDM),
m_numTiles(numTiles),
m_numCycles(numCycles)
{
}
void run() {
switch(m_type) {
case PRODUCER:
for (int j = 0; j < m_numCycles; j++) {
for (int i = 0; i < m_numTiles; i++) {
KisTileSP voidTile = m_srcDM.getTile(i, 0, true);
voidTile->lockForWrite();
QTest::qSleep(1);
voidTile->unlock();
}
QRect cloneRect(0, 0, m_numTiles * 64, 64);
m_dstDM.bitBltRough(&m_srcDM, cloneRect);
- if(j % 50 == 0) qDebug() << "Producer:" << j << "of" << m_numCycles;
+ if(j % 50 == 0) dbgKrita << "Producer:" << j << "of" << m_numCycles;
KisTileDataStore::instance()->debugSwapAll();
}
break;
case CONSUMER_SRC:
for (int j = 0; j < m_numCycles; j++) {
for (int i = 0; i < m_numTiles; i++) {
KisTileSP voidTile = m_srcDM.getTile(i, 0, false);
voidTile->lockForRead();
char temp = *voidTile->data();
Q_UNUSED(temp);
QTest::qSleep(1);
voidTile->unlock();
}
- if(j % 50 == 0) qDebug() << "Consumer_src:" << j << "of" << m_numCycles;
+ if(j % 50 == 0) dbgKrita << "Consumer_src:" << j << "of" << m_numCycles;
KisTileDataStore::instance()->debugSwapAll();
}
break;
case CONSUMER_DST:
for (int j = 0; j < m_numCycles; j++) {
for (int i = 0; i < m_numTiles; i++) {
KisTileSP voidTile = m_dstDM.getTile(i, 0, false);
voidTile->lockForRead();
char temp = *voidTile->data();
Q_UNUSED(temp);
QTest::qSleep(1);
voidTile->unlock();
}
- if(j % 50 == 0) qDebug() << "Consumer_dst:" << j << "of" << m_numCycles;
+ if(j % 50 == 0) dbgKrita << "Consumer_dst:" << j << "of" << m_numCycles;
KisTileDataStore::instance()->debugSwapAll();
}
}
}
private:
Type m_type;
KisTiledDataManager &m_srcDM;
KisTiledDataManager &m_dstDM;
int m_numTiles;
int m_numCycles;
};
void KisLowMemoryTests::readWriteOnSharedTiles()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTiledDataManager dstDM(1, &defaultPixel);
const int NUM_TILES = 10;
const int NUM_CYCLES = 10000;
QThreadPool pool;
pool.setMaxThreadCount(10);
pool.start(new DeadlockyThread(DeadlockyThread::PRODUCER,
srcDM, dstDM, NUM_TILES, NUM_CYCLES));
for (int i = 0; i < 4; i++) {
pool.start(new DeadlockyThread(DeadlockyThread::CONSUMER_SRC,
srcDM, dstDM, NUM_TILES, NUM_CYCLES));
pool.start(new DeadlockyThread(DeadlockyThread::CONSUMER_DST,
srcDM, dstDM, NUM_TILES, NUM_CYCLES));
}
pool.waitForDone();
}
void KisLowMemoryTests::hangingTilesTest()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTileSP srcTile = srcDM.getTile(0, 0, true);
srcTile->lockForWrite();
srcTile->lockForRead();
KisTiledDataManager dstDM(1, &defaultPixel);
dstDM.bitBlt(&srcDM, QRect(0,0,64,64));
KisTileSP dstTile = dstDM.getTile(0, 0, true);
dstTile->lockForRead();
KisTileData *weirdTileData = dstTile->tileData();
quint8 *weirdData = dstTile->data();
QCOMPARE(weirdTileData, srcTile->tileData());
QCOMPARE(weirdData, srcTile->data());
KisTileDataStore::instance()->debugSwapAll();
QCOMPARE(srcTile->tileData(), weirdTileData);
QCOMPARE(dstTile->tileData(), weirdTileData);
QCOMPARE(srcTile->data(), weirdData);
QCOMPARE(dstTile->data(), weirdData);
dstTile->lockForWrite();
KisTileData *cowedTileData = dstTile->tileData();
quint8 *cowedData = dstTile->data();
QVERIFY(cowedTileData != weirdTileData);
KisTileDataStore::instance()->debugSwapAll();
QCOMPARE(srcTile->tileData(), weirdTileData);
QCOMPARE(dstTile->tileData(), cowedTileData);
QCOMPARE(srcTile->data(), weirdData);
QCOMPARE(dstTile->data(), cowedData);
QCOMPARE((int)weirdTileData->m_usersCount, 2);
srcTile->unlock();
srcTile->unlock();
srcTile = 0;
srcDM.clear();
KisTileDataStore::instance()->debugSwapAll();
QCOMPARE(dstTile->tileData(), cowedTileData);
QCOMPARE(dstTile->data(), cowedData);
// two crash tests
QCOMPARE(weirdTileData->data(), weirdData);
quint8 testPixel = *weirdData;
QCOMPARE(testPixel, defaultPixel);
QCOMPARE((int)weirdTileData->m_usersCount, 1);
dstTile->unlock();
dstTile->unlock();
dstTile = 0;
}
QTEST_KDEMAIN(KisLowMemoryTests, GUI)
diff --git a/krita/image/tiles3/tests/kis_memory_window_test.cpp b/krita/image/tiles3/tests/kis_memory_window_test.cpp
index e251de92709..4823616d465 100644
--- a/krita/image/tiles3/tests/kis_memory_window_test.cpp
+++ b/krita/image/tiles3/tests/kis_memory_window_test.cpp
@@ -1,104 +1,104 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_memory_window_test.h"
#include <qtest_kde.h>
#include "kis_debug.h"
#include "tiles3/swap/kis_memory_window.h"
void KisMemoryWindowTest::testWindow()
{
KisMemoryWindow memory(QString(), 1024);
quint8 oddValue = 0xee;
const quint8 chunkLength = 10;
quint8 oddBuf[chunkLength];
memset(oddBuf, oddValue, chunkLength);
KisChunkData chunk1(0, chunkLength);
KisChunkData chunk2(1025, chunkLength);
quint8 *ptr;
ptr = memory.getWriteChunkPtr(chunk1);
memcpy(ptr, oddBuf, chunkLength);
ptr = memory.getWriteChunkPtr(chunk2);
memcpy(ptr, oddBuf, chunkLength);
ptr = memory.getReadChunkPtr(chunk2);
QVERIFY(!memcmp(ptr, oddBuf, chunkLength));
ptr = memory.getWriteChunkPtr(chunk1);
QVERIFY(!memcmp(ptr, oddBuf, chunkLength));
}
void KisMemoryWindowTest::testTopReports()
{
// default window size in 16 MiB
KisMemoryWindow memory(QString(QDir::currentPath()), DEFAULT_WINDOW_SIZE);
// write 1024 chunks 4 MiB each, hi-limit 4GiB
const quint8 oddValue = 0xee;
const qint64 chunkLength = 4 * MiB;
QScopedArrayPointer<quint8> writeBuffer(new quint8[chunkLength]);
memset(writeBuffer.data(), oddValue, chunkLength);
QScopedArrayPointer<quint8> readBuffer(new quint8[chunkLength]);
qint64 maxChunk = 0;
for (int i = 0; i < 1024; i++) {
{
int chunkIndex = qrand() % 1024;
qint64 chunkStart = chunkIndex * chunkLength;
maxChunk = qMax(chunkStart, maxChunk);
quint8 *ptr;
ptr = memory.getWriteChunkPtr(KisChunkData(chunkStart, chunkLength));
memcpy(ptr, writeBuffer.data(), chunkLength);
- qDebug() << "Writing chunk at" << chunkStart / chunkLength << "MiB" << "max" << maxChunk / chunkLength;
+ dbgKrita << "Writing chunk at" << chunkStart / chunkLength << "MiB" << "max" << maxChunk / chunkLength;
QTest::qWait(250);
}
{
int chunkIndex = qrand() % 1024;
qint64 chunkStart = chunkIndex * chunkLength;
quint8 *ptr;
ptr = memory.getReadChunkPtr(KisChunkData(chunkStart, chunkLength));
memcpy(readBuffer.data(), ptr, chunkLength);
- qDebug() << "Reading chunk at" << chunkStart / chunkLength << "MiB" << "max" << maxChunk / chunkLength;
+ dbgKrita << "Reading chunk at" << chunkStart / chunkLength << "MiB" << "max" << maxChunk / chunkLength;
QTest::qWait(250);
}
}
}
QTEST_KDEMAIN(KisMemoryWindowTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_swapped_data_store_test.cpp b/krita/image/tiles3/tests/kis_swapped_data_store_test.cpp
index 64150e14144..b221ab493ab 100644
--- a/krita/image/tiles3/tests/kis_swapped_data_store_test.cpp
+++ b/krita/image/tiles3/tests/kis_swapped_data_store_test.cpp
@@ -1,135 +1,135 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_swapped_data_store_test.h"
#include <qtest_kde.h>
#include "kis_debug.h"
#include "kis_image_config.h"
#include "tiles3/kis_tile_data.h"
#include "tiles_test_utils.h"
#include "tiles3/kis_tile_data_store.h"
#define COLUMN2COLOR(col) (col%255)
void KisSwappedDataStoreTest::testRoundTrip()
{
const qint32 pixelSize = 1;
const quint8 defaultPixel = 128;
const qint32 NUM_TILES = 10000;
KisImageConfig config;
config.setMaxSwapSize(4);
config.setSwapSlabSize(1);
config.setSwapWindowSize(1);
KisSwappedDataStore store;
QList<KisTileData*> tileDataList;
for(qint32 i = 0; i < NUM_TILES; i++)
tileDataList.append(new KisTileData(pixelSize, &defaultPixel, KisTileDataStore::instance()));
for(qint32 i = 0; i < NUM_TILES; i++) {
KisTileData *td = tileDataList[i];
QVERIFY(memoryIsFilled(defaultPixel, td->data(), TILESIZE));
memset(td->data(), COLUMN2COLOR(i), TILESIZE);
QVERIFY(memoryIsFilled(COLUMN2COLOR(i), td->data(), TILESIZE));
// FIXME: take a lock of the tile data
store.swapOutTileData(td);
}
store.debugStatistics();
for(qint32 i = 0; i < NUM_TILES; i++) {
KisTileData *td = tileDataList[i];
QVERIFY(!td->data());
// TODO: check num clones
// FIXME: take a lock of the tile data
store.swapInTileData(td);
QVERIFY(memoryIsFilled(COLUMN2COLOR(i), td->data(), TILESIZE));
}
store.debugStatistics();
for(qint32 i = 0; i < NUM_TILES; i++)
delete tileDataList[i];
}
void KisSwappedDataStoreTest::processTileData(qint32 column, KisTileData *td, KisSwappedDataStore &store)
{
if(td->data()) {
memset(td->data(), COLUMN2COLOR(column), TILESIZE);
QVERIFY(memoryIsFilled(COLUMN2COLOR(column), td->data(), TILESIZE));
// FIXME: take a lock of the tile data
store.swapOutTileData(td);
}
else {
// TODO: check num clones
// FIXME: take a lock of the tile data
store.swapInTileData(td);
QVERIFY(memoryIsFilled(COLUMN2COLOR(column), td->data(), TILESIZE));
}
}
void KisSwappedDataStoreTest::testRandomAccess()
{
qsrand(10);
const qint32 pixelSize = 1;
const quint8 defaultPixel = 128;
const qint32 NUM_CYCLES = 50000;
const qint32 NUM_TILES = 10000;
KisImageConfig config;
config.setMaxSwapSize(40);
config.setSwapSlabSize(1);
config.setSwapWindowSize(1);
KisSwappedDataStore store;
QList<KisTileData*> tileDataList;
for(qint32 i = 0; i < NUM_TILES; i++)
tileDataList.append(new KisTileData(pixelSize, &defaultPixel, KisTileDataStore::instance()));
for(qint32 i = 0; i < NUM_CYCLES; i++) {
if(!(i%5000))
- qDebug() << i << "of" << NUM_CYCLES;
+ dbgKrita << i << "of" << NUM_CYCLES;
qint32 col = qrand() % NUM_TILES;
KisTileData *td = tileDataList[col];
processTileData(col, td, store);
}
store.debugStatistics();
for(qint32 i = 0; i < NUM_TILES; i++)
delete tileDataList[i];
}
QTEST_KDEMAIN(KisSwappedDataStoreTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_tile_compressors_test.cpp b/krita/image/tiles3/tests/kis_tile_compressors_test.cpp
index eb7c8c52aaa..f2c19fe3591 100644
--- a/krita/image/tiles3/tests/kis_tile_compressors_test.cpp
+++ b/krita/image/tiles3/tests/kis_tile_compressors_test.cpp
@@ -1,184 +1,184 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tile_compressors_test.h"
#include <qtest_kde.h>
#include "tiles3/kis_tiled_data_manager.h"
#include "tiles3/swap/kis_legacy_tile_compressor.h"
#include "tiles3/swap/kis_tile_compressor_2.h"
#include "tiles_test_utils.h"
void KisTileCompressorsTest::doRoundTrip(KisAbstractTileCompressor *compressor)
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
quint8 oddPixel1 = 128;
KisTileSP tile11;
dm.clear(64, 64, 64, 64, &oddPixel1);
tile11 = dm.getTile(1, 1, false);
QVERIFY(memoryIsFilled(oddPixel1, tile11->data(), TILESIZE));
KoStoreFake fakeStore;
KisFakePaintDeviceWriter writer(&fakeStore);
bool retval = compressor->writeTile(tile11, writer);
Q_ASSERT(retval);
tile11 = 0;
fakeStore.startReading();
dm.clear();
tile11 = dm.getTile(1, 1, false);
QVERIFY(memoryIsFilled(defaultPixel, tile11->data(), TILESIZE));
tile11 = 0;
bool res = compressor->readTile(fakeStore.device(), &dm);
Q_ASSERT(res);
Q_UNUSED(res);
tile11 = dm.getTile(1, 1, false);
QVERIFY(memoryIsFilled(oddPixel1, tile11->data(), TILESIZE));
tile11 = 0;
}
void KisTileCompressorsTest::doLowLevelRoundTrip(KisAbstractTileCompressor *compressor)
{
const qint32 pixelSize = 1;
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
/**
* A small hack to acquire a standalone tile data.
* globalTileDataStore is not exported out of kritaimage.so,
* so we get it from the data manager
*/
KisTiledDataManager dm(pixelSize, &oddPixel1);
KisTileSP tile = dm.getTile(0, 0, true);
tile->lockForWrite();
KisTileData *td = tile->tileData();
QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));
qint32 bufferSize = compressor->tileDataBufferSize(td);
quint8 *buffer = new quint8[bufferSize];
qint32 bytesWritten;
compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
- qDebug() << ppVar(bytesWritten);
+ dbgKrita << ppVar(bytesWritten);
memset(td->data(), oddPixel2, TILESIZE);
QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));
compressor->decompressTileData(buffer, bytesWritten, td);
QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));
delete[] buffer;
tile->unlock();
}
void KisTileCompressorsTest::doLowLevelRoundTripIncompressible(KisAbstractTileCompressor *compressor)
{
const qint32 pixelSize = 1;
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QByteArray incompressibleArray = file.readAll();
/**
* A small hack to acquire a standalone tile data.
* globalTileDataStore is not exported out of kritaimage.so,
* so we get it from the data manager
*/
KisTiledDataManager dm(pixelSize, &oddPixel1);
KisTileSP tile = dm.getTile(0, 0, true);
tile->lockForWrite();
KisTileData *td = tile->tileData();
QVERIFY(memoryIsFilled(oddPixel1, td->data(), TILESIZE));
memcpy(td->data(), incompressibleArray.data(), TILESIZE);
QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));
qint32 bufferSize = compressor->tileDataBufferSize(td);
quint8 *buffer = new quint8[bufferSize];
qint32 bytesWritten;
compressor->compressTileData(td, buffer, bufferSize, bytesWritten);
- qDebug() << ppVar(bytesWritten);
+ dbgKrita << ppVar(bytesWritten);
memset(td->data(), oddPixel2, TILESIZE);
QVERIFY(memoryIsFilled(oddPixel2, td->data(), TILESIZE));
compressor->decompressTileData(buffer, bytesWritten, td);
QVERIFY(!memcmp(td->data(), incompressibleArray.data(), TILESIZE));
delete[] buffer;
tile->unlock();
}
void KisTileCompressorsTest::testRoundTripLegacy()
{
KisAbstractTileCompressor *compressor = new KisLegacyTileCompressor();
doRoundTrip(compressor);
delete compressor;
}
void KisTileCompressorsTest::testLowLevelRoundTripLegacy()
{
KisAbstractTileCompressor *compressor = new KisLegacyTileCompressor();
doLowLevelRoundTrip(compressor);
delete compressor;
}
void KisTileCompressorsTest::testRoundTrip2()
{
KisAbstractTileCompressor *compressor = new KisTileCompressor2();
doRoundTrip(compressor);
delete compressor;
}
void KisTileCompressorsTest::testLowLevelRoundTrip2()
{
KisAbstractTileCompressor *compressor = new KisTileCompressor2();
doLowLevelRoundTrip(compressor);
delete compressor;
}
void KisTileCompressorsTest::testLowLevelRoundTripIncompressible2()
{
KisAbstractTileCompressor *compressor = new KisTileCompressor2();
doLowLevelRoundTripIncompressible(compressor);
delete compressor;
}
QTEST_KDEMAIN(KisTileCompressorsTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp b/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp
index 3877bbf314d..822914e7b5e 100644
--- a/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp
+++ b/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp
@@ -1,110 +1,110 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tile_data_pooler_test.h"
#include <qtest_kde.h>
#include "tiles3/kis_tiled_data_manager.h"
#include "tiles3/kis_tile_data_store.h"
#include "tiles3/kis_tile_data_store_iterators.h"
#include "tiles3/kis_tile_data_pooler.h"
#ifdef DEBUG_TILES
#define PRETTY_TILE(idx, td) \
- qDebug() << "tile" << i \
+ dbgKrita << "tile" << i \
<< "\tusers" << td->numUsers() \
<< "\tclones" << td->m_clonesStack.size() \
<< "\tage" << td->age();
#else
#define PRETTY_TILE(idx, td)
#endif
void KisTileDataPoolerTest::testCycles()
{
const qint32 pixelSize = 1;
quint8 defaultPixel = 128;
KisTileDataStore::instance()->debugClear();
for(int i = 0; i < 12; i++) {
KisTileData *td =
KisTileDataStore::instance()->createDefaultTileData(pixelSize, &defaultPixel);
for(int j = 0; j < 1 + (2 - i % 3); j++) {
td->acquire();
}
if(!(i/6)) {
td->markOld();
}
if(!((i / 3) & 1)) {
td->m_clonesStack.push(new KisTileData(*td));
}
PRETTY_TILE(i, td);
}
{
KisTileDataPooler pooler(KisTileDataStore::instance(), 5);
pooler.start();
pooler.kick();
pooler.kick();
QTest::qSleep(500);
pooler.terminatePooler();
}
int i = 0;
KisTileData *item;
KisTileDataStoreIterator *iter =
KisTileDataStore::instance()->beginIteration();
while(iter->hasNext()) {
item = iter->next();
int expectedClones;
switch(i) {
case 6:
case 7:
case 10:
expectedClones = 1;
break;
case 9:
expectedClones = 2;
break;
default:
expectedClones = 0;
}
PRETTY_TILE(i, item);
QCOMPARE(item->m_clonesStack.size(), expectedClones);
i++;
}
KisTileDataStore::instance()->endIteration(iter);
KisTileDataStore::instance()->debugClear();
}
QTEST_KDEMAIN(KisTileDataPoolerTest, NoGUI)
diff --git a/krita/image/tiles3/tests/kis_tiled_data_manager_test.cpp b/krita/image/tiles3/tests/kis_tiled_data_manager_test.cpp
index 6ac57b4f4e2..ab6bd5eef9e 100644
--- a/krita/image/tiles3/tests/kis_tiled_data_manager_test.cpp
+++ b/krita/image/tiles3/tests/kis_tiled_data_manager_test.cpp
@@ -1,807 +1,807 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tiled_data_manager_test.h"
#include <qtest_kde.h>
#include "tiles3/kis_tiled_data_manager.h"
#include "tiles_test_utils.h"
bool KisTiledDataManagerTest::checkHole(quint8* buffer,
quint8 holeColor, QRect holeRect,
quint8 backgroundColor, QRect backgroundRect)
{
for(qint32 y = backgroundRect.y(); y <= backgroundRect.bottom(); y++) {
for(qint32 x = backgroundRect.x(); x <= backgroundRect.right(); x++) {
quint8 expectedColor = holeRect.contains(x,y) ? holeColor : backgroundColor;
if(*buffer != expectedColor) {
- qDebug() << "Expected" << expectedColor << "but found" << *buffer;
+ dbgKrita << "Expected" << expectedColor << "but found" << *buffer;
return false;
}
buffer++;
}
}
return true;
}
bool KisTiledDataManagerTest::checkTilesShared(KisTiledDataManager *srcDM,
KisTiledDataManager *dstDM,
bool takeOldSrc,
bool takeOldDst,
QRect tilesRect)
{
for(qint32 row = tilesRect.y(); row <= tilesRect.bottom(); row++) {
for(qint32 col = tilesRect.x(); col <= tilesRect.right(); col++) {
KisTileSP srcTile = takeOldSrc ? srcDM->getOldTile(col, row)
: srcDM->getTile(col, row, false);
KisTileSP dstTile = takeOldDst ? dstDM->getOldTile(col, row)
: dstDM->getTile(col, row, false);
if(srcTile->tileData() != dstTile->tileData()) {
- qDebug() << "Expected tile data (" << col << row << ")"
+ dbgKrita << "Expected tile data (" << col << row << ")"
<< srcTile->extent()
<< srcTile->tileData()
<< "but found" << dstTile->tileData();
- qDebug() << "Expected" << srcTile->data()[0] << "but found" << dstTile->data()[0];
+ dbgKrita << "Expected" << srcTile->data()[0] << "but found" << dstTile->data()[0];
return false;
}
}
}
return true;
}
bool KisTiledDataManagerTest::checkTilesNotShared(KisTiledDataManager *srcDM,
KisTiledDataManager *dstDM,
bool takeOldSrc,
bool takeOldDst,
QRect tilesRect)
{
for(qint32 row = tilesRect.y(); row <= tilesRect.bottom(); row++) {
for(qint32 col = tilesRect.x(); col <= tilesRect.right(); col++) {
KisTileSP srcTile = takeOldSrc ? srcDM->getOldTile(col, row)
: srcDM->getTile(col, row, false);
KisTileSP dstTile = takeOldDst ? dstDM->getOldTile(col, row)
: dstDM->getTile(col, row, false);
if(srcTile->tileData() == dstTile->tileData()) {
- qDebug() << "Expected tiles not be shared:"<< srcTile->extent();
+ dbgKrita << "Expected tiles not be shared:"<< srcTile->extent();
return false;
}
}
}
return true;
}
void KisTiledDataManagerTest::testUndoingNewTiles()
{
// "growing extent bug"
const QRect nullRect(qint32_MAX,qint32_MAX,0,0);
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTileSP emptyTile = srcDM.getTile(0, 0, false);
QCOMPARE(srcDM.extent(), nullRect);
KisMementoSP memento0 = srcDM.getMemento();
KisTileSP createdTile = srcDM.getTile(0, 0, true);
srcDM.commit();
QCOMPARE(srcDM.extent(), QRect(0,0,64,64));
srcDM.rollback(memento0);
QCOMPARE(srcDM.extent(), nullRect);
}
void KisTiledDataManagerTest::testPurgedAndEmptyTransactions()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
quint8 oddPixel1 = 128;
QRect rect(0,0,512,512);
QRect clearRect1(50,50,100,100);
QRect clearRect2(150,50,100,100);
quint8 *buffer = new quint8[rect.width()*rect.height()];
// purged transaction
KisMementoSP memento0 = srcDM.getMemento();
srcDM.clear(clearRect1, &oddPixel1);
srcDM.purgeHistory(memento0);
memento0 = 0;
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect1,
defaultPixel, rect));
// one more purged transaction
KisMementoSP memento1 = srcDM.getMemento();
srcDM.clear(clearRect2, &oddPixel1);
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2,
defaultPixel, rect));
srcDM.purgeHistory(memento1);
memento1 = 0;
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2,
defaultPixel, rect));
// empty one
KisMementoSP memento2 = srcDM.getMemento();
srcDM.commit();
srcDM.rollback(memento2);
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2,
defaultPixel, rect));
// now check that everything works still
KisMementoSP memento3 = srcDM.getMemento();
srcDM.setExtent(clearRect2);
srcDM.commit();
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect2,
defaultPixel, rect));
srcDM.rollback(memento3);
srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2,
defaultPixel, rect));
}
void KisTiledDataManagerTest::testUnversionedBitBlt()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTiledDataManager dstDM(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
QRect rect(0,0,512,512);
QRect cloneRect(81,80,250,250);
QRect tilesRect(2,2,3,3);
srcDM.clear(rect, &oddPixel1);
dstDM.clear(rect, &oddPixel2);
dstDM.bitBlt(&srcDM, cloneRect);
quint8 *buffer = new quint8[rect.width()*rect.height()];
dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, cloneRect,
oddPixel2, rect));
delete[] buffer;
// Test whether tiles became shared
QVERIFY(checkTilesShared(&srcDM, &dstDM, false, false, tilesRect));
}
void KisTiledDataManagerTest::testVersionedBitBlt()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM1(1, &defaultPixel);
KisTiledDataManager srcDM2(1, &defaultPixel);
KisTiledDataManager dstDM(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
quint8 oddPixel3 = 130;
quint8 oddPixel4 = 131;
QRect rect(0,0,512,512);
QRect cloneRect(81,80,250,250);
QRect tilesRect(2,2,3,3);
KisMementoSP memento1 = srcDM1.getMemento();
srcDM1.clear(rect, &oddPixel1);
srcDM2.clear(rect, &oddPixel2);
dstDM.clear(rect, &oddPixel3);
KisMementoSP memento2 = dstDM.getMemento();
dstDM.bitBlt(&srcDM1, cloneRect);
QVERIFY(checkTilesShared(&srcDM1, &dstDM, false, false, tilesRect));
QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect));
QVERIFY(checkTilesNotShared(&dstDM, &dstDM, true, false, tilesRect));
dstDM.commit();
QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect));
KisMementoSP memento3 = srcDM2.getMemento();
srcDM2.clear(rect, &oddPixel4);
KisMementoSP memento4 = dstDM.getMemento();
dstDM.bitBlt(&srcDM2, cloneRect);
QVERIFY(checkTilesShared(&srcDM2, &dstDM, false, false, tilesRect));
QVERIFY(checkTilesNotShared(&srcDM2, &srcDM2, true, false, tilesRect));
QVERIFY(checkTilesNotShared(&dstDM, &dstDM, true, false, tilesRect));
dstDM.commit();
QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect));
dstDM.rollback(memento4);
QVERIFY(checkTilesShared(&srcDM1, &dstDM, false, false, tilesRect));
QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect));
QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect));
dstDM.rollforward(memento4);
QVERIFY(checkTilesShared(&srcDM2, &dstDM, false, false, tilesRect));
QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect));
QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect));
}
void KisTiledDataManagerTest::testBitBltOldData()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTiledDataManager dstDM(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
QRect rect(0,0,512,512);
QRect cloneRect(81,80,250,250);
QRect tilesRect(2,2,3,3);
quint8 *buffer = new quint8[rect.width()*rect.height()];
KisMementoSP memento1 = srcDM.getMemento();
srcDM.clear(rect, &oddPixel1);
srcDM.commit();
dstDM.bitBltOldData(&srcDM, cloneRect);
dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, cloneRect,
defaultPixel, rect));
KisMementoSP memento2 = srcDM.getMemento();
srcDM.clear(rect, &oddPixel2);
dstDM.bitBltOldData(&srcDM, cloneRect);
srcDM.commit();
dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, cloneRect,
defaultPixel, rect));
delete[] buffer;
}
void KisTiledDataManagerTest::testBitBltRough()
{
quint8 defaultPixel = 0;
KisTiledDataManager srcDM(1, &defaultPixel);
KisTiledDataManager dstDM(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
quint8 oddPixel3 = 130;
QRect rect(0,0,512,512);
QRect cloneRect(81,80,250,250);
QRect actualCloneRect(64,64,320,320);
QRect tilesRect(1,1,4,4);
srcDM.clear(rect, &oddPixel1);
dstDM.clear(rect, &oddPixel2);
dstDM.bitBltRough(&srcDM, cloneRect);
quint8 *buffer = new quint8[rect.width()*rect.height()];
dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, actualCloneRect,
oddPixel2, rect));
// Test whether tiles became shared
QVERIFY(checkTilesShared(&srcDM, &dstDM, false, false, tilesRect));
// check bitBltRoughOldData
KisMementoSP memento1 = srcDM.getMemento();
srcDM.clear(rect, &oddPixel3);
dstDM.bitBltRoughOldData(&srcDM, cloneRect);
srcDM.commit();
dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height());
QVERIFY(checkHole(buffer, oddPixel1, actualCloneRect,
oddPixel2, rect));
delete[] buffer;
}
void KisTiledDataManagerTest::testTransactions()
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
quint8 oddPixel3 = 130;
KisTileSP tile00;
KisTileSP oldTile00;
// Create a named transaction: versioning is enabled
KisMementoSP memento1 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel1);
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
// Create an anonymous transaction: versioning is disabled
dm.commit();
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
dm.clear(0, 0, 64, 64, &oddPixel2);
// Versioning is disabled, i said! >:)
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
// And the last round: named transaction:
KisMementoSP memento2 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel3);
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel3, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
}
void KisTiledDataManagerTest::testPurgeHistory()
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
quint8 oddPixel3 = 130;
quint8 oddPixel4 = 131;
KisMementoSP memento1 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel1);
dm.commit();
KisMementoSP memento2 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel2);
KisTileSP tile00;
KisTileSP oldTile00;
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
dm.purgeHistory(memento1);
/**
* Nothing nas changed in the visible state of the data manager
*/
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
dm.commit();
dm.purgeHistory(memento2);
/**
* We've removed all the history of the device, so it
* became "unversioned".
* NOTE: the return value for getOldTile() when there is no
* history present is a subject for change
*/
tile00 = dm.getTile(0, 0, false);
oldTile00 = dm.getOldTile(0, 0);
QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE));
tile00 = oldTile00 = 0;
/**
* Just test we won't crash when the memento is not
* present in history anymore
*/
KisMementoSP memento3 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel3);
dm.commit();
KisMementoSP memento4 = dm.getMemento();
dm.clear(0, 0, 64, 64, &oddPixel4);
dm.commit();
dm.rollback(memento4);
dm.purgeHistory(memento3);
dm.purgeHistory(memento4);
}
void KisTiledDataManagerTest::testUndoSetDefaultPixel()
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
quint8 oddPixel1 = 128;
quint8 oddPixel2 = 129;
QRect fillRect(0,0,64,64);
KisTileSP tile00;
KisTileSP tile10;
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(defaultPixel, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE));
KisMementoSP memento1 = dm.getMemento();
dm.clear(fillRect, &oddPixel1);
dm.commit();
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE));
KisMementoSP memento2 = dm.getMemento();
dm.setDefaultPixel(&oddPixel2);
dm.commit();
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel2, tile10->data(), TILESIZE));
dm.rollback(memento2);
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE));
dm.rollback(memento1);
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(defaultPixel, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE));
dm.rollforward(memento1);
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE));
dm.rollforward(memento2);
tile00 = dm.getTile(0, 0, false);
tile10 = dm.getTile(1, 0, false);
QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE));
QVERIFY(memoryIsFilled(oddPixel2, tile10->data(), TILESIZE));
}
//#include <valgrind/callgrind.h>
void KisTiledDataManagerTest::benchmarkReadOnlyTileLazy()
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
const qint32 numTilesToTest = 1000000;
//CALLGRIND_START_INSTRUMENTATION;
QBENCHMARK_ONCE {
for(qint32 i = 0; i < numTilesToTest; i++) {
KisTileSP tile = dm.getTile(i, i, false);
}
}
//CALLGRIND_STOP_INSTRUMENTATION;
}
class KisSimpleClass : public KisShared
{
qint64 m_int;
};
typedef KisSharedPtr<KisSimpleClass> KisSimpleClassSP;
void KisTiledDataManagerTest::benchmarkSharedPointers()
{
const qint32 numIterations = 2 * 1000000;
//CALLGRIND_START_INSTRUMENTATION;
QBENCHMARK_ONCE {
for(qint32 i = 0; i < numIterations; i++) {
KisSimpleClassSP pointer = new KisSimpleClass;
pointer = 0;
}
}
//CALLGRIND_STOP_INSTRUMENTATION;
}
void KisTiledDataManagerTest::benchmarkCOWImpl()
{
const int pixelSize = 8;
quint8 defaultPixel[pixelSize];
memset(defaultPixel, 1, pixelSize);
KisTiledDataManager dm(pixelSize, defaultPixel);
KisMementoSP memento1 = dm.getMemento();
/**
* Imagine a regular image of 4096x2048 pixels
* (64x32 tiles)
*/
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 64; j++) {
KisTileSP tile = dm.getTile(j, i, true);
tile->lockForWrite();
tile->unlock();
}
}
dm.commit();
QTest::qSleep(500);
KisMementoSP memento2 = dm.getMemento();
QTest::qSleep(500);
QBENCHMARK {
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 64; j++) {
KisTileSP tile = dm.getTile(j, i, true);
tile->lockForWrite();
tile->unlock();
}
}
}
dm.commit();
}
void KisTiledDataManagerTest::benchmarkCOWNoPooler()
{
KisTileDataStore::instance()->testingSuspendPooler();
QTest::qSleep(500);
benchmarkCOWImpl();
KisTileDataStore::instance()->testingResumePooler();
QTest::qSleep(500);
}
void KisTiledDataManagerTest::benchmarkCOWWithPooler()
{
benchmarkCOWImpl();
}
/******************* Stress job ***********************/
//#define NUM_CYCLES 9000
#define NUM_CYCLES 10000
#define NUM_TYPES 12
#define TILE_DIMENSION 64
/**
* The data manager has partial guarantees of reentrancy. That is
* you can call any arbitrary number of methods concurrently as long
* as their access areas do not intersect.
*
* Though the rule can be quite tricky -- some of the methods always
* use entire image as their access area, so they cannot be called
* concurrently in any circumstances.
* The examples are: clear(), commit(), rollback() and etc...
*/
#define run_exclusive(lock, _i) for(_i = 0, (lock).lockForWrite(); _i < 1; _i++, (lock).unlock())
#define run_concurrent(lock, _i) for(_i = 0, (lock).lockForRead(); _i < 1; _i++, (lock).unlock())
//#define run_exclusive(lock, _i) while(0)
//#define run_concurrent(lock, _i) while(0)
class KisStressJob : public QRunnable
{
public:
KisStressJob(KisTiledDataManager &dataManager, QRect rect, QReadWriteLock &_lock)
: m_accessRect(rect), dm(dataManager), lock(_lock)
{
}
void run() {
qsrand(QTime::currentTime().msec());
for(qint32 i = 0; i < NUM_CYCLES; i++) {
qint32 type = qrand() % NUM_TYPES;
qint32 t;
switch(type) {
case 0:
run_concurrent(lock,t) {
quint8 *buf;
buf = new quint8[dm.pixelSize()];
memcpy(buf, dm.defaultPixel(), dm.pixelSize());
dm.setDefaultPixel(buf);
delete[] buf;
}
break;
case 1:
case 2:
run_concurrent(lock,t) {
KisTileSP tile;
tile = dm.getTile(m_accessRect.x() / TILE_DIMENSION,
m_accessRect.y() / TILE_DIMENSION, false);
tile->lockForRead();
tile->unlock();
tile = dm.getTile(m_accessRect.x() / TILE_DIMENSION,
m_accessRect.y() / TILE_DIMENSION, true);
tile->lockForWrite();
tile->unlock();
tile = dm.getOldTile(m_accessRect.x() / TILE_DIMENSION,
m_accessRect.y() / TILE_DIMENSION);
tile->lockForRead();
tile->unlock();
}
break;
case 3:
run_concurrent(lock,t) {
QRect newRect = dm.extent();
Q_UNUSED(newRect);
}
break;
case 4:
run_concurrent(lock,t) {
dm.clear(m_accessRect.x(), m_accessRect.y(),
m_accessRect.width(), m_accessRect.height(), 4);
}
break;
case 5:
run_concurrent(lock,t) {
quint8 *buf;
buf = new quint8[m_accessRect.width() * m_accessRect.height() *
dm.pixelSize()];
dm.readBytes(buf, m_accessRect.x(), m_accessRect.y(),
m_accessRect.width(), m_accessRect.height());
dm.writeBytes(buf, m_accessRect.x(), m_accessRect.y(),
m_accessRect.width(), m_accessRect.height());
delete[] buf;
}
break;
case 6:
run_concurrent(lock,t) {
quint8 oddPixel = 13;
KisTiledDataManager srcDM(1, &oddPixel);
dm.bitBlt(&srcDM, m_accessRect);
}
break;
case 7:
case 8:
run_exclusive(lock,t) {
m_memento = dm.getMemento();
dm.clear(m_accessRect.x(), m_accessRect.y(),
m_accessRect.width(), m_accessRect.height(), 2);
dm.commit();
dm.rollback(m_memento);
dm.rollforward(m_memento);
dm.purgeHistory(m_memento);
m_memento = 0;
}
break;
case 9:
run_exclusive(lock,t) {
bool b = dm.hasCurrentMemento();
Q_UNUSED(b);
}
break;
case 10:
run_exclusive(lock,t) {
dm.clear();
}
break;
case 11:
run_exclusive(lock,t) {
dm.setExtent(m_accessRect);
}
break;
}
}
}
private:
KisMementoSP m_memento;
QRect m_accessRect;
KisTiledDataManager &dm;
QReadWriteLock &lock;
};
void KisTiledDataManagerTest::stressTest()
{
quint8 defaultPixel = 0;
KisTiledDataManager dm(1, &defaultPixel);
QReadWriteLock lock;
QThreadPool pool;
pool.setMaxThreadCount(NUM_TYPES);
QRect accessRect(0,0,100,100);
for(qint32 i = 0; i < NUM_TYPES; i++) {
KisStressJob *job = new KisStressJob(dm, accessRect, lock);
pool.start(job);
accessRect.translate(100, 0);
}
pool.waitForDone();
}
QTEST_KDEMAIN(KisTiledDataManagerTest, NoGUI)
diff --git a/krita/image/tiles3/tests/tiles_test_utils.h b/krita/image/tiles3/tests/tiles_test_utils.h
index 74eb9216880..8af94f93fad 100644
--- a/krita/image/tiles3/tests/tiles_test_utils.h
+++ b/krita/image/tiles3/tests/tiles_test_utils.h
@@ -1,88 +1,89 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TILES_TEST_UTILS_H
#define TILES_TEST_UTILS_H
#include <KoStore_p.h>
#include <kis_paint_device_writer.h>
+#include <kis_debug.h>
class KisFakePaintDeviceWriter : public KisPaintDeviceWriter {
public:
KisFakePaintDeviceWriter(KoStore *store)
: m_store(store)
{
}
bool write(const QByteArray &data) {
return (m_store->write(data) == data.length());
}
bool write(const char* data, qint64 length) {
return (m_store->write(data, length) == length);
}
KoStore *m_store;
};
class KoStoreFake : public KoStore
{
public:
KoStoreFake() : KoStore(KoStore::Write) {
d_ptr->stream = &m_buffer;
d_ptr->isOpen = true;
m_buffer.open(QIODevice::ReadWrite);
}
~KoStoreFake() {
// Oh, no, please do not clean anything! :)
d_ptr->stream = 0;
d_ptr->isOpen = false;
}
void startReading() {
m_buffer.seek(0);
d_ptr->mode = KoStore::Read;
}
bool openWrite(const QString&) { return true; }
bool openRead(const QString&) { return true; }
bool closeRead() { return true; }
bool closeWrite() { return true; }
bool enterRelativeDirectory(const QString&) { return true; }
bool enterAbsoluteDirectory(const QString&) { return true; }
bool fileExists(const QString&) const { return true; }
private:
QBuffer m_buffer;
};
bool memoryIsFilled(quint8 c, quint8 *mem, qint32 size)
{
for(; size > 0; size--)
if(*(mem++) != c) {
- qDebug() << "Expected" << c << "but found" << *(mem-1);
+ dbgKrita << "Expected" << c << "but found" << *(mem-1);
return false;
}
return true;
}
#define TILESIZE 64*64
#endif /* TILES_TEST_UTILS_H */
diff --git a/krita/integration/kimgio/kra.cpp b/krita/integration/kimgio/kra.cpp
index a2422fd1555..57409e30bc6 100644
--- a/krita/integration/kimgio/kra.cpp
+++ b/krita/integration/kimgio/kra.cpp
@@ -1,117 +1,117 @@
/* This file is part of the KDE project
Copyright (C) 2013 Boudewijn Rempt <boud@valdyas.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser 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 code is based on Thacher Ulrich PSD loading code released
on public domain. See: http://tulrich.com/geekstuff/
*/
#include "kra.h"
#include <kzip.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <QImage>
#include <QIODevice>
#include <QFile>
#include <kis_group_layer.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kra/kis_kra_loader.h>
KraHandler::KraHandler()
{
}
bool KraHandler::canRead() const
{
if (canRead(device())) {
setFormat("kra");
return true;
}
return false;
}
bool KraHandler::read(QImage *image)
{
KZip zip(device());
if (!zip.open(QIODevice::ReadOnly)) return false;
const KArchiveEntry *entry = zip.directory()->entry("mergedimage.png");
if (!entry || !entry->isFile()) return false;
const KZipFileEntry* fileZipEntry = static_cast<const KZipFileEntry*>(entry);
image->loadFromData(fileZipEntry->data(), "PNG");
return true;
}
bool KraHandler::write(const QImage &)
{
// TODO Stub!
return false;
}
QByteArray KraHandler::name() const
{
return "kra";
}
bool KraHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("KraHandler::canRead() called with no device");
return false;
}
KZip zip(device);
if (!zip.open(QIODevice::ReadOnly)) return false;
const KArchiveEntry *entry = zip.directory()->entry("mimetype");
if (!entry || !entry->isFile()) return false;
const KZipFileEntry* fileZipEntry = static_cast<const KZipFileEntry*>(entry);
return (qstrcmp(fileZipEntry->data().constData(), "application/x-krita") == 0);
}
class KraPlugin : public QImageIOPlugin
{
public:
QStringList keys() const;
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
};
QStringList KraPlugin::keys() const
{
return QStringList() << "kra" << "KRA";
}
QImageIOPlugin::Capabilities KraPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
Q_UNUSED(device);
if (format == "kra" || format == "KRA")
return Capabilities(CanRead);
else
return 0;
}
QImageIOHandler *KraPlugin::create(QIODevice *device, const QByteArray &format) const
{
QImageIOHandler *handler = new KraHandler;
handler->setDevice(device);
handler->setFormat(format);
return handler;
}
Q_EXPORT_STATIC_PLUGIN(KraPlugin)
Q_EXPORT_PLUGIN2(Kra, KraPlugin)
diff --git a/krita/integration/kimgio/ora.cpp b/krita/integration/kimgio/ora.cpp
index 1f2b4a580fb..098164e3f93 100644
--- a/krita/integration/kimgio/ora.cpp
+++ b/krita/integration/kimgio/ora.cpp
@@ -1,120 +1,120 @@
/* This file is part of the KDE project
Copyright (C) 2013 Boudewijn Rempt <boud@valdyas.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser 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 code is based on Thacher Ulrich PSD loading code released
on public domain. See: http://tulrich.com/geekstuff/
*/
#include "ora.h"
#include <QImage>
#include <QScopedPointer>
#include <kzip.h>
#include <kis_image.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_open_raster_stack_load_visitor.h>
#include "ora_load_context.h"
#include <kis_paint_layer.h>
-#include <kdebug.h>
+#include <kis_debug.h>
OraHandler::OraHandler()
{
}
bool OraHandler::canRead() const
{
if (canRead(device())) {
setFormat("ora");
return true;
}
return false;
}
bool OraHandler::read(QImage *image)
{
KZip zip(device());
if (!zip.open(QIODevice::ReadOnly)) return false;
const KArchiveEntry *entry = zip.directory()->entry("mergedimage.png");
if (!entry || !entry->isFile()) return false;
const KZipFileEntry* fileZipEntry = static_cast<const KZipFileEntry*>(entry);
image->loadFromData(fileZipEntry->data(), "PNG");
return true;
}
bool OraHandler::write(const QImage &)
{
// TODO Stub!
return false;
}
QByteArray OraHandler::name() const
{
return "ora";
}
bool OraHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("KraHandler::canRead() called with no device");
return false;
}
KZip zip(device);
if (!zip.open(QIODevice::ReadOnly)) return false;
const KArchiveEntry *entry = zip.directory()->entry("mimetype");
if (!entry || !entry->isFile()) return false;
const KZipFileEntry* fileZipEntry = static_cast<const KZipFileEntry*>(entry);
return (qstrcmp(fileZipEntry->data().constData(), "image/openraster") == 0);
}
class OraPlugin : public QImageIOPlugin
{
public:
QStringList keys() const;
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
};
QStringList OraPlugin::keys() const
{
return QStringList() << "ora" << "ORA";
}
QImageIOPlugin::Capabilities OraPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
Q_UNUSED(device);
if (format == "ora" || format == "ORA")
return Capabilities(CanRead);
else
return 0;
}
QImageIOHandler *OraPlugin::create(QIODevice *device, const QByteArray &format) const
{
QImageIOHandler *handler = new OraHandler;
handler->setDevice(device);
handler->setFormat(format);
return handler;
}
Q_EXPORT_STATIC_PLUGIN(OraPlugin)
Q_EXPORT_PLUGIN2(Ora, OraPlugin)
diff --git a/krita/kis_crash_handler.cpp b/krita/kis_crash_handler.cpp
index 35544f47848..b48b964b0dd 100644
--- a/krita/kis_crash_handler.cpp
+++ b/krita/kis_crash_handler.cpp
@@ -1,211 +1,211 @@
/*
* Copyright (C) 2008-2009 Hyves (Startphone Ltd.)
* Copyright (c) 2014 Boudewijn Rempt <boud@kde.org>
*
* 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 US
*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Breakpad integration
*
* The Initial Developer of the Original Code is
* Ted Mielczarek <ted.mielczarek@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Josh Aas <josh@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "kis_crash_handler.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QDesktopServices>
#include <QTextStream>
#ifdef Q_WS_MAC
#include <string>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <client/mac/handler/exception_handler.h>
#elif defined Q_OS_MAC
#include <string.h>
#include <windows.h>
#include <iostream>
#include <client/windows/handler/exception_handler.h>
#elif defined HAVE_X11
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <client/linux/handler/exception_handler.h>
#else
#error "Breakpad does not exist for this platform!"
#endif
#include <stdlib.h>
#include <time.h>
#ifdef Q_OS_MAC
#ifndef UNICODE
#define UNICODE
#endif
typedef wchar_t HD_CHAR;
// sort of arbitrary, but MAX_PATH is kinda small
#define HD_MAX_PATH 4096
#define CRASH_REPORTER_BINARY L"crashhandler.exe"
#else
typedef char HD_CHAR;
#define CRASH_REPORTER_BINARY "crashhandler"
#endif
static google_breakpad::ExceptionHandler *exceptionHandler = 0;
// if this is true, we pass the exception on to the OS crash reporter
// static bool showOSCrashReporter = false;
// Note: we've crashed so we cannot use the heap here. So we cannot use any Q* class.
static bool startCrashReporter(const HD_CHAR *dumpPath, const HD_CHAR *minidumpID, void *context,
#ifdef Q_OS_MAC
EXCEPTION_POINTERS *exceptionInfo,
MDRawAssertionInfo *assertion,
#endif
bool succeeded) {
if (!succeeded) {
return false;
}
#ifdef Q_OS_MAC
wchar_t command[(HD_MAX_PATH * 2) + 6];
wcscpy(command, CRASH_REPORTER_BINARY L" \"");
wcscat(command, dumpPath);
wcscat(command, L"\" \"");
wcscat(command, minidumpID);
wcscat(command, L"\"");
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
ZeroMemory(&pi, sizeof(pi));
if (CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
TerminateProcess(GetCurrentProcess(), 1);
}
return false;
#else
pid_t pid = fork();
if (pid == -1) {
return false;
} else if (pid == 0) {
execl(CRASH_REPORTER_BINARY, CRASH_REPORTER_BINARY, dumpPath, minidumpID, (char*)0);
}
#ifdef HAVE_X11
abort();
#endif
return true;
#endif
}
// Copied from Qt's QString class because of weird linking errors
int toWCharArray(const QString str, wchar_t *array)
{
if (sizeof(wchar_t) == sizeof(QChar)) {
memcpy(array, str.utf16(), sizeof(wchar_t)*str.length());
return str.length();
} else {
wchar_t *a = array;
const unsigned short *uc = str.utf16();
for (int i = 0; i < str.length(); ++i) {
uint u = uc[i];
if (u >= 0xd800 && u < 0xdc00 && i < str.length()-1) {
ushort low = uc[i+1];
if (low >= 0xdc00 && low < 0xe000) {
++i;
u = (u - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
}
}
*a = wchar_t(u);
++a;
}
return a - array;
}
}
KisCrashHandler::KisCrashHandler()
{
QString tempPath = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
#ifdef Q_OS_MAC
- qDebug() << "Installing CrashHandler" << tempPath;
+ dbgKrita << "Installing CrashHandler" << tempPath;
typedef std::basic_string<wchar_t> wstring;
wstring str;
str.resize(tempPath.length());
str.resize(toWCharArray(tempPath, &(*str.begin())));
exceptionHandler = new google_breakpad::ExceptionHandler(str, 0,
startCrashReporter, 0,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#else
- qDebug() << "Installing CrashHandler"; // do not remove this line; it is needed to make it work on linux.
+ dbgKrita << "Installing CrashHandler"; // do not remove this line; it is needed to make it work on linux.
exceptionHandler = new google_breakpad::ExceptionHandler(tempPath.toStdString(), 0,
startCrashReporter, 0,
true);
#endif
Q_CHECK_PTR(exceptionHandler);
}
KisCrashHandler::~KisCrashHandler()
{
delete exceptionHandler;
}
diff --git a/krita/libbrush/abrStructv2.cpp b/krita/libbrush/abrStructv2.cpp
index 311fcc0a601..f361f966752 100644
--- a/krita/libbrush/abrStructv2.cpp
+++ b/krita/libbrush/abrStructv2.cpp
@@ -1,278 +1,278 @@
/*
* Copyright (c) 2010 Valek Filippov <frob@gnome.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QString>
#include <QFile>
#include <QDataStream>
-#include <QDebug>
+#include <kis_debug.h>
#include <iostream>
const QString PATT = "patt";
const QString DESC = "desc";
const QString VLLS = "VlLs";
const QString TEXT = "TEXT";
const QString OBJC = "Objc";
const QString UNTF = "UntF";
const QString BOOL = "bool";
const QString LONG = "long";
const QString DOUB = "doub";
const QString ENUM = "enum";
enum enumFuncNames {
P_PATT,
P_DESC,
P_VLLS,
P_TEXT,
P_OBJC,
P_UNTF,
P_BOOL,
P_LONG,
P_DOUB,
P_ENUM
};
static QHash<QString, enumFuncNames> types;
static QString p_patt(QDataStream &buf)
{
// didn't rev.engineered yet
Q_UNUSED(buf);
return QString("");
}
static QString p_desc(QDataStream &buf)
{
// convert 4 bytes as big-endian unsigned long
quint32 size;
// 22 + 4
buf >> size;
buf.skipRawData(22);
return QString::number(size);
}
static QString p_vlls(QDataStream &buf)
{
quint32 size;
buf >> size;
return QString::number(size);
}
static QString p_text(QDataStream &buf)
{
quint32 size;
buf >> size;
ushort * text = new ushort[size];
for (int i = 0; i < size; i++) {
buf >> text[i];
}
return QString::fromUtf16(text, size);
}
static QString p_objc(QDataStream &buf)
{
quint32 objvallen;
buf >> objvallen;
char * objval = new char[objvallen * 2 + 1];
buf.readRawData(objval, objvallen * 2);
objval[ objvallen * 2 ] = '\0';
quint32 size;
buf >> size;
if (size == 0) {
size = 4;
}
char * name = new char[size + 1];
buf.readRawData(name, size);
name[size] = '\0';
quint32 value;
buf >> value;
return QString::fromLatin1(name) + ' ' + QString::number(value);
}
static QString p_untf(QDataStream &buf)
{
char * type = new char[5];
buf.readRawData(type, 4);
type[4] = '\0';
double value;
buf >> value;
return QString::fromLatin1(type) + ' ' + QString::number(value);
}
static QString p_bool(QDataStream &buf)
{
//# ord converts 1 byte number
char byte;
buf.device()->getChar(&byte);
if (byte) return QString("1");
else return QString("0");
}
static QString p_doub(QDataStream &buf)
{
// unpack 8 bytes ieee 754 value to floating point number
double value;
buf >> value;
return QString::number(value);
}
static QString p_enum(QDataStream &buf)
{
quint32 size1, size2;
buf >> size1;
if (size1 == 0) {
size1 = 4;
}
char * name1 = new char[size1 + 1];
buf.readRawData(name1, size1);
name1[size1] = '\0';
buf >> size2 ;
if (size2 == 0) {
size2 = 4;
}
char * name2 = new char[size2 + 1];
buf.readRawData(name2, size2);
name2[size2] = '\0';
return QString::fromLatin1(name1) + ' ' + QString::fromLatin1(name2);
}
static quint32 parseEntry(QDataStream &buf)
{
quint32 nlen;
QString value;
buf >> nlen;
if (nlen == 0) {
nlen = 4;
}
if (nlen == 1331849827) { // "Objc"
value = p_objc(buf); // TODO: port
- qDebug() << "Objc " << value;
+ dbgKrita << "Objc " << value;
}
else {
// read char with nlen bytes and convert to String
char * name = new char[ nlen + 1 ];
int status = buf.readRawData(name, nlen);
if (status == -1) {
- qDebug() << "Error, name can't be readed";
+ dbgKrita << "Error, name can't be readed";
}
name[nlen] = '\0';
char * type = new char[5];
status = buf.readRawData(type, 4);
type[4] = '\0';
QString key = QString::fromLatin1(type);
if (types.contains(key)) {
enumFuncNames enumName = types[key];
switch (enumName) {
case P_PATT: value = p_patt(buf); break;
case P_DESC: value = p_desc(buf); break;
case P_VLLS: value = p_vlls(buf); break;
case P_TEXT: value = p_text(buf); break;
case P_OBJC: value = p_objc(buf); break;
case P_UNTF: value = p_untf(buf); break;
case P_BOOL: value = p_bool(buf); break;
case P_LONG: value = p_vlls(buf); break; // yes vlls, it is not typo
case P_DOUB: value = p_doub(buf); break;
case P_ENUM: value = p_enum(buf); break;
- default: qDebug() << "Freak error occurred!"; break;
+ default: dbgKrita << "Freak error occurred!"; break;
}
- qDebug() << name << type << value;
+ dbgKrita << name << type << value;
}
else {
- qDebug() << "Unknown key:\t" << name << type;
- //qDebug() << p_unkn(buf);
+ dbgKrita << "Unknown key:\t" << name << type;
+ //dbgKrita << p_unkn(buf);
return -1;
}
}
return 0;
}
static void parse(QString fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
- qDebug() << "Can't open file " << fileName;
+ dbgKrita << "Can't open file " << fileName;
return;
}
QDataStream buf(&file);
// offset in bytes
short int vermaj, vermin;
buf >> vermaj;
buf >> vermin;
- qDebug() << "Version: " << vermaj << "." << vermin;
+ dbgKrita << "Version: " << vermaj << "." << vermin;
int index = file.readAll().indexOf("8BIMdesc");
buf.device()->seek(index);
int status = 0;
while (!buf.atEnd()) {
status = parseEntry(buf);
if (status == -1) {
// something to break the parsing with fail?
- qDebug() << "Finishing with fail...";
+ dbgKrita << "Finishing with fail...";
break;
}
}
}
int main(int argc, const char * argv[])
{
QString fileName;
if (argc != 2) {
fileName = "test.abr";
}
else {
fileName = QString::fromLatin1(argv[1]);
}
types.insert(PATT, P_PATT);
types.insert(DESC, P_DESC);
types.insert(VLLS, P_VLLS);
types.insert(TEXT, P_TEXT);
types.insert(OBJC, P_OBJC);
types.insert(UNTF, P_UNTF);
types.insert(BOOL, P_BOOL);
types.insert(LONG, P_LONG);
types.insert(DOUB, P_DOUB);
types.insert(ENUM, P_ENUM);
parse(fileName);
return 0;
}
diff --git a/krita/libbrush/abr_struct_parser.cpp b/krita/libbrush/abr_struct_parser.cpp
index 3a381c8e5f0..baf7a8e0942 100644
--- a/krita/libbrush/abr_struct_parser.cpp
+++ b/krita/libbrush/abr_struct_parser.cpp
@@ -1,306 +1,306 @@
/*
* Copyright (c) 2010 Valek Filippov <frob@gnome.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QString>
#include <QFile>
#include <QDataStream>
-#include <QDebug>
+#include <kis_debug.h>
#include <QDomDocument>
#include "abr_struct_parser.h"
#include "kis_abr_translator.h"
AbrStructParser::AbrStructParser()
{
m_types.insert(PATT, P_PATT);
m_types.insert(DESC, P_DESC);
m_types.insert(VLLS, P_VLLS);
m_types.insert(TEXT, P_TEXT);
m_types.insert(OBJC, P_OBJC);
m_types.insert(UNTF, P_UNTF);
m_types.insert(BOOL, P_BOOL);
m_types.insert(LONG, P_LONG);
m_types.insert(DOUB, P_DOUB);
m_types.insert(ENUM, P_ENUM);
m_types.insert(TDTA, P_TDTA);
}
AbrStructParser::~AbrStructParser()
{
}
QString AbrStructParser::p_patt(QDataStream &buf)
{
// didn't rev.engineered yet
Q_UNUSED(buf);
return QString("");
}
QString AbrStructParser::p_tdta(QDataStream &buf)
{
quint32 size;
buf >> size;
ushort * text = new ushort[size];
for (quint32 i = 0; i < size; i++) {
buf >> text[i];
}
return "(tdta:" + QString::number(size) + ')' + QString::fromUtf16(text, size);
}
QString AbrStructParser::p_desc(QDataStream &buf)
{
// convert 4 bytes as big-endian unsigned long
quint32 size;
// 22 + 4
buf >> size;
buf.skipRawData(22);
return QString::number(size);
}
QString AbrStructParser::p_long(QDataStream &buf)
{
quint32 size;
buf >> size;
return QString::number(size);
}
QString AbrStructParser::p_vlls(QDataStream &buf)
{
quint32 size;
buf >> size;
return QString::number(size);
}
QString AbrStructParser::p_text(QDataStream &buf)
{
quint32 size;
buf >> size;
ushort * text = new ushort[size + 1];
for (quint32 i = 0; i < size; i++) {
buf >> text[i];
}
text[size] = '\0';
return QString::fromUtf16(text);
}
QString AbrStructParser::p_objc(QDataStream &buf)
{
// here we lost some data definitly
// objnamelen is always 1 and objname is empty string
quint32 objnamelen;
buf >> objnamelen;
char * objname = new char[objnamelen * 2 + 1];
buf.readRawData(objname, objnamelen * 2);
objname[ objnamelen * 2 ] = '\0';
Q_ASSERT(objnamelen == 1);
quint32 objtypelen;
buf >> objtypelen;
if (objtypelen == 0) {
objtypelen = 4;
}
char * typeName = new char[objtypelen + 1];
buf.readRawData(typeName, objtypelen);
typeName [objtypelen] = '\0';
quint32 value;
buf >> value;
//return QString::fromLatin1( objname ) + ' ' + QString::fromLatin1(typeName) + ' ' + QString::number(value);
return QString::fromLatin1(typeName) + ' ' + QString::number(value);
}
QString AbrStructParser::p_untf(QDataStream &buf)
{
char * type = new char[5];
buf.readRawData(type, 4);
type[4] = '\0';
double value;
buf >> value;
return QString::fromLatin1(type) + ' ' + QString::number(value);
}
QString AbrStructParser::p_bool(QDataStream &buf)
{
//# ord converts 1 byte number
char byte;
buf.device()->getChar(&byte);
if (byte)
return QString("1");
else
return QString("0");
}
QString AbrStructParser::p_doub(QDataStream &buf)
{
// unpack 8 bytes ieee 754 value to floating point number
double value;
buf >> value;
return QString::number(value);
}
QString AbrStructParser::p_enum(QDataStream &buf)
{
quint32 size1, size2;
buf >> size1;
if (size1 == 0) {
size1 = 4;
}
char * name1 = new char[size1 + 1];
buf.readRawData(name1, size1);
name1[size1] = '\0';
buf >> size2 ;
if (size2 == 0) {
size2 = 4;
}
char * name2 = new char[size2 + 1];
buf.readRawData(name2, size2);
name2[size2] = '\0';
return QString::fromLatin1(name1) + ' ' + QString::fromLatin1(name2);
}
quint32 AbrStructParser::parseEntry(QDataStream &buf)
{
quint32 nlen;
buf >> nlen;
if (nlen == 0) {
nlen = 4;
}
QString value;
if (nlen == MAGIC_OBJC_LENGTH) {
value = p_objc(buf);
- qDebug() << ABR_PRESET_START << ABR_OBJECT << value;
+ dbgKrita << ABR_PRESET_START << ABR_OBJECT << value;
// start to create the preset here
m_translator.addEntry(ABR_PRESET_START, ABR_OBJECT, value);
}
else {
// read char with nlen bytes and convert to String
char * name = new char[ nlen + 1 ];
int status = buf.readRawData(name, nlen);
if (status == -1) {
- qDebug() << "Error, name can't be readed";
+ dbgKrita << "Error, name can't be readed";
}
name[nlen] = '\0';
char * type = new char[5];
status = buf.readRawData(type, 4);
type[4] = '\0';
QString key = QString::fromLatin1(type);
if (m_types.contains(key)) {
enumFuncNames enumName = m_types[key];
switch (enumName) {
case P_PATT: value = p_patt(buf); break;
case P_DESC: value = p_desc(buf); break;
case P_VLLS: value = p_vlls(buf); break;
case P_TEXT: value = p_text(buf); break;
case P_OBJC: value = p_objc(buf); break;
case P_UNTF: value = p_untf(buf); break;
case P_BOOL: value = p_bool(buf); break;
case P_LONG: value = p_long(buf); break;
case P_DOUB: value = p_doub(buf); break;
case P_ENUM: value = p_enum(buf); break;
case P_TDTA: value = p_tdta(buf); break;
- default: qDebug() << "Freak error occurred!"; break;
+ default: dbgKrita << "Freak error occurred!"; break;
}
QString attributeName = QString::fromLatin1(name);
- //qDebug() << attributeName << key << value;
+ //dbgKrita << attributeName << key << value;
m_translator.addEntry(attributeName, key, value);
// airbrush is the last parsed attribute of the preset
if (attributeName == ABR_AIRBRUSH) {
m_translator.finishPreset();
- qDebug() << m_translator.toString();
+ dbgKrita << m_translator.toString();
}
}
else {
- qDebug() << "Unknown key:\t" << name << type;
- //qDebug() << p_unkn(buf);
+ dbgKrita << "Unknown key:\t" << name << type;
+ //dbgKrita << p_unkn(buf);
return -1;
}
}
return 0;
}
void AbrStructParser::parse(QString fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
- qDebug() << "Can't open file " << fileName;
+ dbgKrita << "Can't open file " << fileName;
return;
}
QDataStream buf(&file);
// offset in bytes
short int vermaj, vermin;
buf >> vermaj;
buf >> vermin;
- qDebug() << "Version: " << vermaj << "." << vermin;
+ dbgKrita << "Version: " << vermaj << "." << vermin;
int index = file.readAll().indexOf("8BIMdesc");
buf.device()->seek(index);
int status = 0;
while (!buf.atEnd()) {
status = parseEntry(buf);
if (status == -1) {
// something to break the parsing with fail?
- qDebug() << "Finishing with fail...";
+ dbgKrita << "Finishing with fail...";
break;
}
}
- qDebug() << m_doc.toString();
+ dbgKrita << m_doc.toString();
}
int main(int argc, const char * argv[])
{
QString fileName;
if (argc != 2) {
fileName = "test.abr";
}
else {
fileName = QString::fromLatin1(argv[1]);
}
AbrStructParser parser;
parser.parse(fileName);
return 0;
}
diff --git a/krita/libbrush/abrbrush.cpp b/krita/libbrush/abrbrush.cpp
index abcfa4e88f5..79ea1981949 100644
--- a/krita/libbrush/abrbrush.cpp
+++ b/krita/libbrush/abrbrush.cpp
@@ -1,538 +1,538 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2007 Eric Lamarque <eric.lamarque@free.fr>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Initial code from Marco Lamberto abr2gbr (http://the.sunnyspot.org/gimp/)
// Ported code from http://registry.gimp.org/node/126
#include <QColor>
#include <QtEndian>
#include <QByteArray>
-#include <QDebug>
+#include <kis_debug.h>
#include <QString>
#include <QFile>
#include <netinet/in.h>
#include <QImage>
struct AbrInfo {
//big endian
short version;
short subversion;
// count of the images (brushes) in the abr file
short count;
};
/// save the QImages as png files to directory image_tests
static void saveToQImage(char * buffer, qint32 width, qint32 height, const QString fileName)
{
// create 8-bit indexed image
QImage img(width, height, QImage::Format_Indexed8);
// every image needs color index table
QVector<QRgb> table;
for (int i = 0; i < 255; ++i) table.append(qRgb(i, i, i));
img.setColorTable(table);
for (int y = 0; y < height; y++) {
memcpy(img.scanLine(y), buffer + width * y, width);
}
QImage out(width, height, QImage::Format_ARGB32);
QColor black(Qt::black);
out.fill(black.rgb());
out.setAlphaChannel(img);
out.save("image_tests/" + fileName + ".png");
}
static qint32 rle_decode(QDataStream & abr, char *buffer, qint32 height)
{
qint32 n;
char ptmp;
char ch;
int i, j, c;
short *cscanline_len;
char *data = buffer;
// read compressed size foreach scanline
cscanline_len = new short[ height ];
for (i = 0; i < height; i++) {
// short
abr >> cscanline_len[i];
}
// unpack each scanline data
for (i = 0; i < height; i++) {
for (j = 0; j < cscanline_len[i];) {
// char
abr.device()->getChar(&ptmp);
n = ptmp;
j++;
if (n >= 128) // force sign
n -= 256;
if (n < 0) { // copy the following char -n + 1 times
if (n == -128) // it's a nop
continue;
n = -n + 1;
// char
abr.device()->getChar(&ch);
j++;
for (c = 0; c < n; c++, data++) {
*data = ch;
}
} else {
// read the following n + 1 chars (no compr)
for (c = 0; c < n + 1; c++, j++, data++) {
// char
abr.device()->getChar(data);
}
}
}
}
delete [] cscanline_len;
return 0;
}
static QString abr_v1_brush_name(const QString filename, qint32 id)
{
QString result = filename;
int pos = filename.lastIndexOf('.');
result.remove(pos, 4);
QTextStream(&result) << "_" << id;
return result;
}
static bool abr_supported_content(AbrInfo *abr_hdr)
{
switch (abr_hdr->version) {
case 1:
case 2:
return true;
break;
case 6:
if (abr_hdr->subversion == 1 || abr_hdr->subversion == 2)
return true;
break;
}
return false;
}
static bool abr_reach_8BIM_section(QDataStream & abr, const QString name)
{
char tag[4];
char tagname[5];
qint32 section_size = 0;
int r;
// find 8BIMname section
while (!abr.atEnd()) {
r = abr.readRawData(tag, 4);
if (r != 4) {
- qDebug() << "Error: Cannot read 8BIM tag ";
+ dbgKrita << "Error: Cannot read 8BIM tag ";
return false;
}
if (strncmp(tag, "8BIM", 4)) {
- qDebug() << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos();
+ dbgKrita << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos();
return false;
}
r = abr.readRawData(tagname, 4);
if (r != 4) {
- qDebug() << "Error: Cannot read 8BIM tag name";
+ dbgKrita << "Error: Cannot read 8BIM tag name";
return false;
}
tagname[4] = '\0';
QString s1 = QString::fromLatin1(tagname, 4);
if (!s1.compare(name)) {
return true;
}
// long
abr >> section_size;
abr.device()->seek(abr.device()->pos() + section_size);
}
return true;
}
static qint32 find_sample_count_v6(QDataStream & abr, AbrInfo *abr_info)
{
qint64 origin;
qint32 sample_section_size;
qint32 sample_section_end;
qint32 samples = 0;
qint32 data_start;
qint32 brush_size;
qint32 brush_end;
if (!abr_supported_content(abr_info))
return 0;
origin = abr.device()->pos();
if (!abr_reach_8BIM_section(abr, "samp")) {
// reset to origin
abr.device()->seek(origin);
return 0;
}
// long
abr >> sample_section_size;
sample_section_end = sample_section_size + abr.device()->pos();
data_start = abr.device()->pos();
while ((!abr.atEnd()) && (abr.device()->pos() < sample_section_end)) {
// read long
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) brush_end++;
abr.device()->seek(abr.device()->pos() + brush_end);
samples++;
}
// set stream to samples data
abr.device()->seek(data_start);
- //qDebug() <<"samples : "<< samples;
+ //dbgKrita <<"samples : "<< samples;
return samples;
}
static bool abr_read_content(QDataStream & abr, AbrInfo *abr_hdr)
{
abr >> abr_hdr->version;
abr_hdr->subversion = 0;
abr_hdr->count = 0;
switch (abr_hdr->version) {
case 1:
case 2:
abr >> abr_hdr->count;
break;
case 6:
abr >> abr_hdr->subversion;
abr_hdr->count = find_sample_count_v6(abr, abr_hdr);
break;
default:
// unknown versions
break;
}
// next bytes in abr are samples data
return true;
}
static QString abr_read_ucs2_text(QDataStream & abr)
{
quint64 name_size;
quint64 buf_size;
uint i;
/* two-bytes characters encoded (UCS-2)
* format:
* long : size - number of characters in string
* data : zero terminated UCS-2 string
*/
// long
abr >> name_size;
if (name_size == 0) {
return QString();
}
//buf_size = name_size * 2;
buf_size = name_size;
//name_ucs2 = (char*) malloc (buf_size * sizeof (char));
//name_ucs2 = new char[buf_size];
ushort * name_ucs2 = new ushort[buf_size];
for (i = 0; i < buf_size ; i++) {
//* char*/
//abr >> name_ucs2[i];
// I will use ushort as that is input to fromUtf16
abr >> name_ucs2[i];
}
QString name_utf8 = QString::fromUtf16(name_ucs2, buf_size);
delete [] name_ucs2;
return name_utf8;
}
static quint32 abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
qint32 brush_size;
qint32 brush_end;
qint32 complement_to_4;
qint32 next_brush;
qint32 top, left, bottom, right;
short depth;
char compression;
qint32 width, height;
qint32 size;
qint32 layer_ID = -1;
char *buffer;
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) {
brush_end++;
}
complement_to_4 = brush_end - brush_size;
next_brush = abr.device()->pos() + brush_end;
// discard key
abr.device()->seek(abr.device()->pos() + 37);
if (abr_hdr->subversion == 1) {
// discard short coordinates and unknown short
abr.device()->seek(abr.device()->pos() + 10);
}
else {
// discard unknown bytes
abr.device()->seek(abr.device()->pos() + 264);
}
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
// remove .abr and add some id, so something like test.abr -> test_12345
QString name = abr_v1_brush_name(filename, id);
buffer = (char*)malloc(size);
// data decoding
if (!compression) {
// not compressed - read raw bytes as brush data
//fread (buffer, size, 1, abr);
abr.readRawData(buffer, size);
}
else {
rle_decode(abr, buffer, height);
}
saveToQImage(buffer, width, height , name);
free(buffer);
abr.device()->seek(next_brush);
layer_ID = id;
return layer_ID;
}
static qint32 abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
short brush_type;
qint32 brush_size;
qint32 next_brush;
qint32 top, left, bottom, right;
qint16 depth;
char compression;
QString name;
qint32 width, height;
qint32 size;
qint32 layer_ID = -1;
char *buffer;
// short
abr >> brush_type;
// long
abr >> brush_size;
next_brush = abr.device()->pos() + brush_size;
switch (brush_type) {
case 1:
// computed brush
// FIXME: support it!
- qDebug() << "WARNING: computed brush unsupported, skipping.";
+ dbgKrita << "WARNING: computed brush unsupported, skipping.";
abr.device()->seek(abr.device()->pos() + next_brush);
// TODO: test also this one abr.skipRawData(next_brush);
break;
case 2:
// sampled brush
// discard 4 misc bytes and 2 spacing bytes
abr.device()->seek(abr.device()->pos() + 6);
if (abr_hdr->version == 2)
name = abr_read_ucs2_text(abr);
if (name.isNull()) {
name = abr_v1_brush_name(filename, id);
}
// discard 1 byte for antialiasing and 4 x short for short bounds
abr.device()->seek(abr.device()->pos() + 9);
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
/* FIXME: support wide brushes */
if (height > 16384) {
- qDebug() << "WARNING: wide brushes not supported";
+ dbgKrita << "WARNING: wide brushes not supported";
abr.device()->seek(next_brush);
break;
}
buffer = (char*)malloc(size);
if (!compression) {
// not compressed - read raw bytes as brush data
//fread (buffer, size, 1, abr);
abr.readRawData(buffer, size);
}
else {
rle_decode(abr, buffer, height);
}
saveToQImage(buffer, width, height, name);
free(buffer);
break;
default:
- qDebug() << "WARNING: unknown brush type, skipping.";
+ dbgKrita << "WARNING: unknown brush type, skipping.";
abr.device()->seek(next_brush);
}
return layer_ID;
}
static qint32 abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
qint32 layer_ID = -1;
switch (abr_hdr->version) {
case 1:
case 2:
layer_ID = abr_brush_load_v12(abr, abr_hdr, filename, image_ID, id);
break;
case 6:
layer_ID = abr_brush_load_v6(abr, abr_hdr, filename, image_ID, id);
break;
}
return layer_ID;
}
static qint32 abr_load(const QString filename)
{
QFile file(filename);
AbrInfo abr_hdr;
qint32 image_ID;
int i;
qint32 layer_ID;
// check if the file is open correctly
if (!file.open(QIODevice::ReadOnly)) {
- qDebug() << "Can't open file " << filename;
+ dbgKrita << "Can't open file " << filename;
return -1;
}
QDataStream abr(&file);
if (!abr_read_content(abr, &abr_hdr)) {
- qDebug() << "Error: cannot parse ABR file: " << filename;
+ dbgKrita << "Error: cannot parse ABR file: " << filename;
return -1;
}
if (!abr_supported_content(&abr_hdr)) {
- qDebug() << "ERROR: unable to decode abr format version " << abr_hdr.version << "(subver " << abr_hdr.subversion << ")";
+ dbgKrita << "ERROR: unable to decode abr format version " << abr_hdr.version << "(subver " << abr_hdr.subversion << ")";
return -1;
}
if (abr_hdr.count == 0) {
- qDebug() << "ERROR: no sample brush found in " << filename;
+ dbgKrita << "ERROR: no sample brush found in " << filename;
return -1;
}
image_ID = 123456;
for (i = 0; i < abr_hdr.count; i++) {
layer_ID = abr_brush_load(abr, &abr_hdr, filename, image_ID, i + 1);
if (layer_ID == -1) {
- qDebug() << "Warning: problem loading brush #" << i << " in " << filename;
+ dbgKrita << "Warning: problem loading brush #" << i << " in " << filename;
}
- qDebug() << i + 1 << " / " << abr_hdr.count;
+ dbgKrita << i + 1 << " / " << abr_hdr.count;
}
file.close();
return image_ID;
}
int main(int argc, const char * argv[])
{
QString fileName;
if (argc != 2) {
fileName = "test.abr";
}
else {
fileName = QString::fromLatin1(argv[1]);
}
abr_load(fileName);
return 0;
}
diff --git a/krita/libbrush/kis_abr_brush_collection.cpp b/krita/libbrush/kis_abr_brush_collection.cpp
index 83cc5f74443..88825848ffd 100644
--- a/krita/libbrush/kis_abr_brush_collection.cpp
+++ b/krita/libbrush/kis_abr_brush_collection.cpp
@@ -1,612 +1,612 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2007 Eric Lamarque <eric.lamarque@free.fr>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <netinet/in.h>
#include "kis_abr_brush_collection.h"
#include "kis_abr_brush.h"
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPoint>
#include <QColor>
#include <QtEndian>
#include <QByteArray>
-#include <QDebug>
+#include <kis_debug.h>
#include <QString>
#include <QBuffer>
#include <klocale.h>
#include <KoColor.h>
struct AbrInfo {
//big endian
short version;
short subversion;
// count of the images (brushes) in the abr file
short count;
};
/// save the QImages as png files to directory image_tests
static QImage convertToQImage(char * buffer, qint32 width, qint32 height)
{
// create 8-bit indexed image
QImage img(width, height, QImage::Format_RGB32);
int pos = 0;
int value = 0;
for (int y = 0; y < height; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(img.scanLine(y));
for (int x = 0; x < width; x++, pos++) {
value = 255 - buffer[pos];
pixel[x] = qRgb(value, value , value);
}
}
return img;
}
static qint32 rle_decode(QDataStream & abr, char *buffer, qint32 height)
{
qint32 n;
char ptmp;
char ch;
int i, j, c;
short *cscanline_len;
char *data = buffer;
// read compressed size foreach scanline
cscanline_len = new short[ height ];
for (i = 0; i < height; i++) {
// short
abr >> cscanline_len[i];
}
// unpack each scanline data
for (i = 0; i < height; i++) {
for (j = 0; j < cscanline_len[i];) {
// char
if (!abr.device()->getChar(&ptmp)) {
break;
}
n = ptmp;
j++;
if (n >= 128) // force sign
n -= 256;
if (n < 0) { // copy the following char -n + 1 times
if (n == -128) // it's a nop
continue;
n = -n + 1;
// char
if (!abr.device()->getChar(&ch)) {
break;
}
j++;
for (c = 0; c < n; c++, data++) {
*data = ch;
}
}
else {
// read the following n + 1 chars (no compr)
for (c = 0; c < n + 1; c++, j++, data++) {
// char
if (!abr.device()->getChar(data)) {
break;
}
}
}
}
}
delete [] cscanline_len;
return 0;
}
static QString abr_v1_brush_name(const QString filename, qint32 id)
{
QString result = filename;
int pos = filename.lastIndexOf('.');
result.remove(pos, 4);
QTextStream(&result) << "_" << id;
return result;
}
static bool abr_supported_content(AbrInfo *abr_hdr)
{
switch (abr_hdr->version) {
case 1:
case 2:
return true;
break;
case 6:
if (abr_hdr->subversion == 1 || abr_hdr->subversion == 2)
return true;
break;
}
return false;
}
static bool abr_reach_8BIM_section(QDataStream & abr, const QString name)
{
char tag[4];
char tagname[5];
qint32 section_size = 0;
int r;
// find 8BIMname section
while (!abr.atEnd()) {
r = abr.readRawData(tag, 4);
if (r != 4) {
- qWarning() << "Error: Cannot read 8BIM tag ";
+ warnKrita << "Error: Cannot read 8BIM tag ";
return false;
}
if (strncmp(tag, "8BIM", 4)) {
- qWarning() << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos();
+ warnKrita << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos();
return false;
}
r = abr.readRawData(tagname, 4);
if (r != 4) {
- qWarning() << "Error: Cannot read 8BIM tag name";
+ warnKrita << "Error: Cannot read 8BIM tag name";
return false;
}
tagname[4] = '\0';
QString s1 = QString::fromLatin1(tagname, 4);
if (!s1.compare(name)) {
return true;
}
// long
abr >> section_size;
abr.device()->seek(abr.device()->pos() + section_size);
}
return true;
}
static qint32 find_sample_count_v6(QDataStream & abr, AbrInfo *abr_info)
{
qint64 origin;
qint32 sample_section_size;
qint32 sample_section_end;
qint32 samples = 0;
qint32 data_start;
qint32 brush_size;
qint32 brush_end;
if (!abr_supported_content(abr_info))
return 0;
origin = abr.device()->pos();
if (!abr_reach_8BIM_section(abr, "samp")) {
// reset to origin
abr.device()->seek(origin);
return 0;
}
// long
abr >> sample_section_size;
sample_section_end = sample_section_size + abr.device()->pos();
if(sample_section_end < 0 || sample_section_end > abr.device()->size())
return 0;
data_start = abr.device()->pos();
while ((!abr.atEnd()) && (abr.device()->pos() < sample_section_end)) {
// read long
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) brush_end++;
qint64 newPos = abr.device()->pos() + brush_end;
if(newPos > 0 && newPos < abr.device()->size()) {
abr.device()->seek(newPos);
}
else
return 0;
samples++;
}
// set stream to samples data
abr.device()->seek(data_start);
- //qDebug() <<"samples : "<< samples;
+ //dbgKrita <<"samples : "<< samples;
return samples;
}
static bool abr_read_content(QDataStream & abr, AbrInfo *abr_hdr)
{
abr >> abr_hdr->version;
abr_hdr->subversion = 0;
abr_hdr->count = 0;
switch (abr_hdr->version) {
case 1:
case 2:
abr >> abr_hdr->count;
break;
case 6:
abr >> abr_hdr->subversion;
abr_hdr->count = find_sample_count_v6(abr, abr_hdr);
break;
default:
// unknown versions
break;
}
// next bytes in abr are samples data
return true;
}
static QString abr_read_ucs2_text(QDataStream & abr)
{
quint32 name_size;
quint32 buf_size;
uint i;
/* two-bytes characters encoded (UCS-2)
* format:
* long : size - number of characters in string
* data : zero terminated UCS-2 string
*/
// long
abr >> name_size;
if (name_size == 0) {
return QString();
}
//buf_size = name_size * 2;
buf_size = name_size;
//name_ucs2 = (char*) malloc (buf_size * sizeof (char));
//name_ucs2 = new char[buf_size];
ushort * name_ucs2 = new ushort[buf_size];
for (i = 0; i < buf_size ; i++) {
//* char*/
//abr >> name_ucs2[i];
// I will use ushort as that is input to fromUtf16
abr >> name_ucs2[i];
}
QString name_utf8 = QString::fromUtf16(name_ucs2, buf_size);
delete [] name_ucs2;
return name_utf8;
}
quint32 KisAbrBrushCollection::abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
qint32 brush_size = 0;
qint32 brush_end = 0;
qint32 next_brush = 0;
qint32 top, left, bottom, right;
top = left = bottom = right = 0;
short depth;
char compression;
qint32 width = 0;
qint32 height = 0;
qint32 size = 0;
qint32 layer_ID = -1;
char *buffer;
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) {
brush_end++;
}
next_brush = abr.device()->pos() + brush_end;
// discard key
abr.device()->seek(abr.device()->pos() + 37);
if (abr_hdr->subversion == 1)
// discard short coordinates and unknown short
abr.device()->seek(abr.device()->pos() + 10);
else
// discard unknown bytes
abr.device()->seek(abr.device()->pos() + 264);
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
// remove .abr and add some id, so something like test.abr -> test_12345
QString name = abr_v1_brush_name(filename, id);
buffer = (char*)malloc(size);
// data decoding
if (!compression) {
// not compressed - read raw bytes as brush data
//fread (buffer, size, 1, abr);
abr.readRawData(buffer, size);
} else {
rle_decode(abr, buffer, height);
}
if (width < quint16_MAX && height < quint16_MAX) {
// filename - filename of the file , e.g. test.abr
// name - test_number_of_the_brush, e.g test_1, test_2
KisAbrBrush* abrBrush = 0;
if (m_abrBrushes.contains(name)) {
abrBrush = m_abrBrushes[name];
}
else {
abrBrush = new KisAbrBrush(name, this);
abrBrush->setMD5(md5());
}
abrBrush->setBrushTipImage(convertToQImage(buffer, width, height));
// XXX: call extra setters on abrBrush for other options of ABR brushes
abrBrush->setValid(true);
abrBrush->setName(name);
m_abrBrushes[name] = abrBrush;
}
free(buffer);
abr.device()->seek(next_brush);
layer_ID = id;
return layer_ID;
}
qint32 KisAbrBrushCollection::abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
short brush_type;
qint32 brush_size;
qint32 next_brush;
qint32 top, left, bottom, right;
qint16 depth;
char compression;
QString name;
qint32 width, height;
qint32 size;
qint32 layer_ID = -1;
char *buffer;
// short
abr >> brush_type;
// long
abr >> brush_size;
next_brush = abr.device()->pos() + brush_size;
if (brush_type == 1) {
// computed brush
// FIXME: support it!
warnKrita << "WARNING: computed brush unsupported, skipping.";
abr.device()->seek(abr.device()->pos() + next_brush);
// TODO: test also this one abr.skipRawData(next_brush);
}
else if (brush_type == 2) {
// sampled brush
// discard 4 misc bytes and 2 spacing bytes
abr.device()->seek(abr.device()->pos() + 6);
if (abr_hdr->version == 2)
name = abr_read_ucs2_text(abr);
if (name.isNull()) {
name = abr_v1_brush_name(filename, id);
}
// discard 1 byte for antialiasing and 4 x short for short bounds
abr.device()->seek(abr.device()->pos() + 9);
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
/* FIXME: support wide brushes */
if (height > 16384) {
warnKrita << "WARNING: wide brushes not supported";
abr.device()->seek(next_brush);
}
else {
buffer = (char*)malloc(size);
if (!compression) {
// not compressed - read raw bytes as brush data
abr.readRawData(buffer, size);
} else {
rle_decode(abr, buffer, height);
}
KisAbrBrush* abrBrush = 0;
if (m_abrBrushes.contains(name)) {
abrBrush = m_abrBrushes[name];
}
else {
abrBrush = new KisAbrBrush(name, this);
abrBrush->setMD5(md5());
}
abrBrush->setBrushTipImage(convertToQImage(buffer, width, height));
// XXX: call extra setters on abrBrush for other options of ABR brushes free (buffer);
abrBrush->setValid(true);
abrBrush->setName(name);
m_abrBrushes[name] = abrBrush;
layer_ID = 1;
}
}
else {
warnKrita << "Unknown ABR brush type, skipping.";
abr.device()->seek(next_brush);
}
return layer_ID;
}
qint32 KisAbrBrushCollection::abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
qint32 layer_ID = -1;
switch (abr_hdr->version) {
case 1:
// fall through, version 1 and 2 are compatible
case 2:
layer_ID = abr_brush_load_v12(abr, abr_hdr, filename, image_ID, id);
break;
case 6:
layer_ID = abr_brush_load_v6(abr, abr_hdr, filename, image_ID, id);
break;
}
return layer_ID;
}
KisAbrBrushCollection::KisAbrBrushCollection(const QString& filename)
: KisBrush(filename)
{
}
bool KisAbrBrushCollection::load()
{
QFile file(filename());
// check if the file is open correctly
if (!file.open(QIODevice::ReadOnly)) {
warnKrita << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KisAbrBrushCollection::loadFromDevice(QIODevice *dev)
{
AbrInfo abr_hdr;
qint32 image_ID;
int i;
qint32 layer_ID;
QByteArray ba = dev->readAll();
QBuffer buf(&ba);
buf.open(QIODevice::ReadOnly);
QDataStream abr(&buf);
if (!abr_read_content(abr, &abr_hdr)) {
warnKrita << "Error: cannot parse ABR file: " << filename();
return false;
}
if (!abr_supported_content(&abr_hdr)) {
warnKrita << "ERROR: unable to decode abr format version " << abr_hdr.version << "(subver " << abr_hdr.subversion << ")";
return false;
}
if (abr_hdr.count == 0) {
errKrita << "ERROR: no sample brush found in " << filename();
return false;
}
image_ID = 123456;
for (i = 0; i < abr_hdr.count; i++) {
layer_ID = abr_brush_load(abr, &abr_hdr, shortFilename(), image_ID, i + 1);
if (layer_ID == -1) {
warnKrita << "Warning: problem loading brush #" << i << " in " << filename();
}
}
return true;
}
bool KisAbrBrushCollection::save()
{
return false;
}
bool KisAbrBrushCollection::saveToDevice(QIODevice */*dev*/) const
{
return false;
}
QImage KisAbrBrushCollection::image() const
{
return QImage();
}
void KisAbrBrushCollection::toXML(QDomDocument& d, QDomElement& e) const
{
Q_UNUSED(d);
Q_UNUSED(e);
// Do nothing...
}
QString KisAbrBrushCollection::defaultFileExtension() const
{
return QString(".abr");
}
diff --git a/krita/libbrush/kis_abr_brush_collection.h b/krita/libbrush/kis_abr_brush_collection.h
index c52ab7cc22b..57969ceadcb 100644
--- a/krita/libbrush/kis_abr_brush_collection.h
+++ b/krita/libbrush/kis_abr_brush_collection.h
@@ -1,91 +1,91 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2007 Eric Lamarque <eric.lamarque@free.fr>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ABR_BRUSH_COLLECTION_H
#define KIS_ABR_BRUSH_COLLECTION_H
#include <QImage>
#include <QVector>
#include <QDataStream>
#include <QString>
-#include <QDebug>
+#include <kis_debug.h>
#include <kis_brush.h>
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_paint_information.h"
class QString;
class QIODevice;
class KisAbrBrush;
struct AbrInfo;
/**
* load a collection of brushes from an abr file
*/
class BRUSH_EXPORT KisAbrBrushCollection : public KisBrush
{
protected:
public:
/// Construct brush to load filename later as brush
KisAbrBrushCollection(const QString& filename);
virtual ~KisAbrBrushCollection() {}
virtual bool load();
virtual bool loadFromDevice(QIODevice *dev);
virtual bool save();
virtual bool saveToDevice(QIODevice* dev) const;
/**
* @return a preview of the brush
*/
virtual QImage image() const;
/**
* @return default file extension for saving the brush
*/
virtual QString defaultFileExtension() const;
QList<KisAbrBrush*> brushes() {
return m_abrBrushes.values();
}
protected:
void toXML(QDomDocument& d, QDomElement& e) const;
private:
qint32 abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
qint32 abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
quint32 abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id);
QMap<QString, KisAbrBrush*> m_abrBrushes;
};
#endif
diff --git a/krita/libbrush/kis_abr_translator.cpp b/krita/libbrush/kis_abr_translator.cpp
index 15c10309ba7..63184a3857d 100644
--- a/krita/libbrush/kis_abr_translator.cpp
+++ b/krita/libbrush/kis_abr_translator.cpp
@@ -1,338 +1,338 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_abr_translator.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QStringList>
KisAbrTranslator::KisAbrTranslator()
{
init();
}
KisAbrTranslator::~KisAbrTranslator()
{
}
void KisAbrTranslator::init()
{
m_root = m_doc.createElement("Preset");
m_root.setAttribute("paintopid", "paintbrush");
}
void KisAbrTranslator::addEntry(const QString& attributeName, const QString& type, const QString& value)
{
// setup object type
// shape dynamics is not separated in the Objc so workaround by attribute name
if (type == ABR_OBJECT ||
attributeName == ABR_USE_TIP_DYNAMICS ||
attributeName == ABR_USE_SCATTER) {
if (m_currentObjectName == ABR_DUAL_BRUSH && attributeName == OBJECT_NAME_BRUSH) {
m_currentObjectName = ABR_DUAL_BRUSH + '_' + attributeName;
}
else {
m_currentObjectName = attributeName;
}
- qDebug() << "---- Object type changed to " << m_currentObjectName;
+ dbgKrita << "---- Object type changed to " << m_currentObjectName;
}
// behaviour according object's attribute name
if (m_currentObjectName == ABR_PRESET_START) {
if (attributeName == ABR_PRESET_START) {
clean();
}
else if (attributeName == ABR_PRESET_NAME) {
m_root.setAttribute("name", value);
}
else {
- //qDebug() << "--Unknown attribute: " << attributeName;
+ //dbgKrita << "--Unknown attribute: " << attributeName;
}
}
else if (m_currentObjectName == OBJECT_NAME_BRUSH) {
m_abrBrushProperties.setupProperty(attributeName, type, value);
// this is not object name but shape dynamics does not start with objc :/
// those objects has been merged with shape attributes due to serialization
// e.g. minumumDiameter belongs to ABR_SZVR and is ABR_USE_TIP_DYNAMICS object
}
else if (m_currentObjectName == ABR_USE_TIP_DYNAMICS ||
m_currentObjectName == ABR_SZVR ||
m_currentObjectName == ABR_ANGLE_DYNAMICS ||
m_currentObjectName == ABR_ROUNDNESS_DYNAMICS) {
m_abrTipDynamics.setupProperty(attributeName, type, value);
}
else if (m_currentObjectName == ABR_USE_SCATTER) {
// TODO
}
else {
- qDebug() << "Unknown attribute of " << m_currentObjectName << "| " << attributeName << type << value << " |";
+ dbgKrita << "Unknown attribute of " << m_currentObjectName << "| " << attributeName << type << value << " |";
}
}
void KisAbrTranslator::finishPreset()
{
m_abrBrushProperties.toXML(m_doc, m_root);
m_abrTipDynamics.toXML(m_doc, m_root);
m_doc.appendChild(m_root);
m_abrTipDynamics.reset();
m_currentObjectName.clear();
}
QString KisAbrTranslator::toString()
{
return m_doc.toString();
}
void KisAbrTranslator::clean()
{
m_doc.clear();
m_root.clear();
init();
}
void AbrBrushProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
{
Q_UNUSED(type);
double valueDbl = 0.0;
QStringList list;
if (attributeName == ABR_BRUSH_DIAMETER ||
attributeName == ABR_BRUSH_HARDNESS ||
attributeName == ABR_BRUSH_ANGLE ||
attributeName == ABR_BRUSH_ROUNDNESS ||
attributeName == ABR_BRUSH_SPACING ||
attributeName == OBJECT_NAME_BRUSH) {
list = value.split(' ');
//e.g. "#Pxl 10" -> ['#Pxl','10']
Q_ASSERT(list.count() == 2);
bool ok;
valueDbl = list.at(1).toDouble(&ok);
Q_ASSERT(ok);
}
if (attributeName == OBJECT_NAME_BRUSH) {
m_brushType = list.at(0);
}
else if (attributeName == ABR_BRUSH_DIAMETER) {
m_diameter = valueDbl;
}
else if (attributeName == ABR_BRUSH_HARDNESS) {
m_hardness = valueDbl;
}
else if (attributeName == ABR_BRUSH_ANGLE) {
m_angle = valueDbl;
}
else if (attributeName == ABR_BRUSH_ROUNDNESS) {
m_roundness = valueDbl;
}
else if (attributeName == ABR_BRUSH_SPACING) {
m_spacing = valueDbl;
}
else if (attributeName == ABR_BRUSH_INTR) {
m_intr = value.toInt();
}
else if (attributeName == ABR_FLIP_X) {
m_flipX = value.toInt();
}
else if (attributeName == ABR_FLIP_Y) {
m_flipY = value.toInt();
}
else {
- qDebug() << "Unknown attribute " << attributeName;
+ dbgKrita << "Unknown attribute " << attributeName;
}
}
// <param name="brush_definition">
// <![CDATA[
// <Brush type="auto_brush" spacing="0.1" angle="0">
// <MaskGenerator radius="5" ratio="1" type="circle" vfade="0.5" spikes="2" hfade="0.5"/>
// </Brush> ]]>
// </param>
void AbrBrushProperties::toXML(QDomDocument& doc, QDomElement& root) const
{
if (m_brushType != BRUSH_TYPE_COMPUTED) {
- qDebug() << m_brushType << "saved as computed brush...";
+ dbgKrita << m_brushType << "saved as computed brush...";
}
QDomDocument d;
QDomElement e = d.createElement("Brush");
QDomElement shapeElement = d.createElement("MaskGenerator");
shapeElement.setAttribute("radius", (m_diameter * 0.5)); // radius == diameter / 2
shapeElement.setAttribute("ratio", m_roundness / 100.0); // roundness in (0..100) to ratio in (0.0..1.0)
shapeElement.setAttribute("hfade", m_hardness / 100.0); // same here
shapeElement.setAttribute("vfade", m_hardness / 100.0); // and here too
shapeElement.setAttribute("spikes", 2); // just circle so far
shapeElement.setAttribute("type", "circle");
e.appendChild(shapeElement);
e.setAttribute("type", "auto_brush");
e.setAttribute("spacing", m_spacing / 100.0); // spacing from 0..1000 to
e.setAttribute("angle", m_angle < 0 ? m_angle + 360.0 : m_angle); // angle from -180..180 to 0..360
e.setAttribute("randomness", 0); // default here
d.appendChild(e);
QDomElement elementParam = doc.createElement("param");
elementParam.setAttribute("name", "brush_definition");
QDomText text = doc.createCDATASection(d.toString());
elementParam.appendChild(text);
root.appendChild(elementParam);
}
AbrTipDynamicsProperties::AbrTipDynamicsProperties()
{
m_groups[ ABR_SZVR ] = &m_sizeVarianceProperties;
m_groups[ ABR_ANGLE_DYNAMICS ] = &m_angleProperties;
m_groups[ ABR_ROUNDNESS_DYNAMICS ] = &m_RoundnessProperties;
Q_ASSERT(m_groupType.isNull());
}
void AbrTipDynamicsProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
{
if (type == ABR_OBJECT) {
if (!m_groups.keys().contains(attributeName)) {
- qDebug() << "Unknown " << type << " in Tip dynamics called " << attributeName << " : " << value;
+ dbgKrita << "Unknown " << type << " in Tip dynamics called " << attributeName << " : " << value;
}
else {
m_groupType = attributeName;
}
return;
}
Q_UNUSED(type);
double valueDbl = 0.0;
QStringList list;
if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER ||
attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS ||
attributeName == ABR_TIP_DYNAMICS_TILT_SCALE
) {
list = value.split(' ');
//e.g. "#Pxl 10" -> ['#Pxl','10']
Q_ASSERT(list.count() == 2);
bool ok;
valueDbl = list.at(1).toDouble(&ok);
Q_ASSERT(ok);
}
if (m_groupType.isNull()) {
if (attributeName == ABR_USE_TIP_DYNAMICS) {
m_useTipDynamics = value.toInt();
}
else if (attributeName == ABR_FLIP_X) {
m_flipX = value.toInt();
}
else if (attributeName == ABR_FLIP_Y) {
m_flipY = value.toInt();
}
else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER) {
m_minumumDiameter = valueDbl;
}
else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS) {
m_minumumRoundness = valueDbl;
}
else if (attributeName == ABR_TIP_DYNAMICS_TILT_SCALE) {
m_tiltScale = valueDbl;
}
else {
- qDebug() << "Unknown attribute for tip dynamics" << attributeName;
+ dbgKrita << "Unknown attribute for tip dynamics" << attributeName;
}
} else {
m_groups[ m_groupType ]->setupProperty(attributeName, type, value);
}
}
void AbrTipDynamicsProperties::toXML(QDomDocument& doc, QDomElement& root) const
{
QDomElement el = doc.createElement("shape_dynamics");
el.setAttribute("useTipDynamics", m_useTipDynamics);
el.setAttribute("flipX", m_flipX);
el.setAttribute("flipY", m_flipY);
root.appendChild(el);
el = doc.createElement("angleDynamics");
el.setAttribute("angleJitter", m_angleProperties.m_sizeJitter);
el.setAttribute("angleController", m_angleProperties.m_bVTy);
el.setAttribute("angleFadeStep", m_angleProperties.m_fadeStep);
root.appendChild(el);
el = doc.createElement("roundnessDynamics");
el.setAttribute("minumumRoundness", m_minumumRoundness);
el.setAttribute("roundnessJitter", m_RoundnessProperties.m_sizeJitter);
el.setAttribute("roundnessController", m_RoundnessProperties.m_bVTy);
el.setAttribute("roundnessFadeStep", m_RoundnessProperties.m_fadeStep);
root.appendChild(el);
el = doc.createElement("sizeDynamics");
el.setAttribute("tiltScale", m_tiltScale);
el.setAttribute("minumumDiameter", m_minumumDiameter);
el.setAttribute("roundnessJitter", m_RoundnessProperties.m_sizeJitter);
el.setAttribute("roundnessController", m_RoundnessProperties.m_bVTy);
el.setAttribute("roundnessFadeStep", m_RoundnessProperties.m_fadeStep);
root.appendChild(el);
}
// <param name="CurveSize"><![CDATA[0,0.257028;1,0.493976;]]></param>
// 0,m_minimumDiameter 1,m_sizeJitter
// <param name="PressureSize">true</param>
// <param name="SizeSensor"><![CDATA[<!DOCTYPE params>
// <params id="fuzzy"/> ]]></param> // controller
void AbrGroupProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
{
Q_UNUSED(type);
double valueDbl = 0.0;
QStringList list;
if (attributeName == ABR_DYNAMICS_JITTER) {
list = value.split(' ');
//e.g. "#Pxl 10" -> ['#Pxl','10']
Q_ASSERT(list.count() == 2);
bool ok;
valueDbl = list.at(1).toDouble(&ok);
Q_ASSERT(ok);
}
if (attributeName == ABR_DYNAMICS_FADE_STEP) {
m_fadeStep = value.toInt();
}
else if (attributeName == ABR_DYNAMICS_JITTER) {
m_sizeJitter = valueDbl;
}
else if (attributeName == ABR_CONTROL) {
m_bVTy = (enumAbrControllers)value.toInt();
}
else {
- qDebug() << "Unknown attribute for Group!" << attributeName;
+ dbgKrita << "Unknown attribute for Group!" << attributeName;
}
}
diff --git a/krita/libbrush/kis_auto_brush.cpp b/krita/libbrush/kis_auto_brush.cpp
index e458940aede..ebc703d17a4 100644
--- a/krita/libbrush/kis_auto_brush.cpp
+++ b/krita/libbrush/kis_auto_brush.cpp
@@ -1,341 +1,341 @@
/*
* Copyright (c) 2004,2007-2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2012 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_auto_brush.h"
#include <kis_debug.h>
#include <math.h>
#include <QRect>
#include <QDomElement>
#include <QtConcurrentMap>
#include <QByteArray>
#include <QBuffer>
#include <QFile>
#include <QFileInfo>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_datamanager.h"
#include "kis_fixed_paint_device.h"
#include "kis_paint_device.h"
#include "kis_paint_information.h"
#include "kis_mask_generator.h"
#include "kis_boundary.h"
#if defined(_WIN32) || defined(_WIN64)
#include <stdlib.h>
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
struct KisAutoBrush::Private {
KisMaskGenerator* shape;
qreal randomness;
qreal density;
int idealThreadCountCached;
};
KisAutoBrush::KisAutoBrush(KisMaskGenerator* as, qreal angle, qreal randomness, qreal density)
: KisBrush()
, d(new Private)
{
d->shape = as;
d->randomness = randomness;
d->density = density;
d->idealThreadCountCached = QThread::idealThreadCount();
setBrushType(MASK);
setWidth(qMax(qreal(1.0), d->shape->width()));
setHeight(qMax(qreal(1.0), d->shape->height()));
QImage image = createBrushPreview();
setBrushTipImage(image);
// Set angle here so brush tip image is generated unrotated
setAngle(angle);
image = createBrushPreview();
setImage(image);
}
KisAutoBrush::~KisAutoBrush()
{
delete d->shape;
delete d;
}
inline void fillPixelOptimized_4bytes(quint8 *color, quint8 *buf, int size)
{
/**
* This version of filling uses low granularity of data transfers
* (32-bit chunks) and internal processor's parallelism. It reaches
* 25% better performance in KisStrokeBenchmark in comparison to
* per-pixel memcpy version (tested on Sandy Bridge).
*/
int block1 = size / 8;
int block2 = size % 8;
quint32 *src = reinterpret_cast<quint32*>(color);
quint32 *dst = reinterpret_cast<quint32*>(buf);
// check whether all buffers are 4 bytes aligned
// (uncomment if experience some problems)
// Q_ASSERT(((qint64)src & 3) == 0);
// Q_ASSERT(((qint64)dst & 3) == 0);
for (int i = 0; i < block1; i++) {
*dst = *src;
*(dst + 1) = *src;
*(dst + 2) = *src;
*(dst + 3) = *src;
*(dst + 4) = *src;
*(dst + 5) = *src;
*(dst + 6) = *src;
*(dst + 7) = *src;
dst += 8;
}
for (int i = 0; i < block2; i++) {
*dst = *src;
dst++;
}
}
inline void fillPixelOptimized_general(quint8 *color, quint8 *buf, int size, int pixelSize)
{
/**
* This version uses internal processor's parallelism and gives
* 20% better performance in KisStrokeBenchmark in comparison to
* per-pixel memcpy version (tested on Sandy Bridge (+20%) and
* on Merom (+10%)).
*/
int block1 = size / 8;
int block2 = size % 8;
for (int i = 0; i < block1; i++) {
quint8 *d1 = buf;
quint8 *d2 = buf + pixelSize;
quint8 *d3 = buf + 2 * pixelSize;
quint8 *d4 = buf + 3 * pixelSize;
quint8 *d5 = buf + 4 * pixelSize;
quint8 *d6 = buf + 5 * pixelSize;
quint8 *d7 = buf + 6 * pixelSize;
quint8 *d8 = buf + 7 * pixelSize;
for (int j = 0; j < pixelSize; j++) {
*(d1 + j) = color[j];
*(d2 + j) = color[j];
*(d3 + j) = color[j];
*(d4 + j) = color[j];
*(d5 + j) = color[j];
*(d6 + j) = color[j];
*(d7 + j) = color[j];
*(d8 + j) = color[j];
}
buf += 8 * pixelSize;
}
for (int i = 0; i < block2; i++) {
memcpy(buf, color, pixelSize);
buf += pixelSize;
}
}
void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst,
KisBrush::ColoringInformation* coloringInformation,
double scaleX, double scaleY, double angle,
const KisPaintInformation& info,
double subPixelX , double subPixelY, qreal softnessFactor) const
{
Q_UNUSED(info);
// Generate the paint device from the mask
const KoColorSpace* cs = dst->colorSpace();
quint32 pixelSize = cs->pixelSize();
// mask dimension methods already includes KisBrush::angle()
int dstWidth = maskWidth(scaleX, angle, subPixelX, subPixelY, info);
int dstHeight = maskHeight(scaleY, angle, subPixelX, subPixelY, info);
QPointF hotSpot = this->hotSpot(scaleX, scaleY, angle, info);
// mask size and hotSpot function take the KisBrush rotation into account
angle += KisBrush::angle();
// if there's coloring information, we merely change the alpha: in that case,
// the dab should be big enough!
if (coloringInformation) {
// old bounds
QRect oldBounds = dst->bounds();
// new bounds. we don't care if there is some extra memory occcupied.
dst->setRect(QRect(0, 0, dstWidth, dstHeight));
if (dstWidth * dstHeight <= oldBounds.width() * oldBounds.height()) {
// just clear the data in dst,
memset(dst->data(), OPACITY_TRANSPARENT_U8, dstWidth * dstHeight * dst->pixelSize());
}
else {
// enlarge the data
dst->initialize();
}
}
else {
if (dst->data() == 0 || dst->bounds().isEmpty()) {
- qWarning() << "Creating a default black dab: no coloring info and no initialized paint device to mask";
+ warnKrita << "Creating a default black dab: no coloring info and no initialized paint device to mask";
dst->clear(QRect(0, 0, dstWidth, dstHeight));
}
Q_ASSERT(dst->bounds().width() >= dstWidth && dst->bounds().height() >= dstHeight);
}
quint8* dabPointer = dst->data();
quint8* color = 0;
if (coloringInformation) {
if (dynamic_cast<PlainColoringInformation*>(coloringInformation)) {
color = const_cast<quint8*>(coloringInformation->color());
}
}
double centerX = hotSpot.x() - 0.5 + subPixelX;
double centerY = hotSpot.y() - 0.5 + subPixelY;
d->shape->setScale(scaleX, scaleY);
d->shape->setSoftness(softnessFactor);
if (coloringInformation) {
if (color && pixelSize == 4) {
fillPixelOptimized_4bytes(color, dabPointer, dstWidth * dstHeight);
}
else if (color) {
fillPixelOptimized_general(color, dabPointer, dstWidth * dstHeight, pixelSize);
}
else {
for (int y = 0; y < dstHeight; y++) {
for (int x = 0; x < dstWidth; x++) {
memcpy(dabPointer, coloringInformation->color(), pixelSize);
coloringInformation->nextColumn();
dabPointer += pixelSize;
}
coloringInformation->nextRow();
}
}
}
MaskProcessingData data(dst, cs, d->randomness, d->density,
centerX, centerY,
angle);
KisBrushMaskApplicatorBase *applicator = d->shape->applicator();
applicator->initializeData(&data);
int jobs = d->idealThreadCountCached;
if (dstHeight > 100 && jobs >= 4) {
int splitter = dstHeight / jobs;
QVector<QRect> rects;
for (int i = 0; i < jobs - 1; i++) {
rects << QRect(0, i * splitter, dstWidth, splitter);
}
rects << QRect(0, (jobs - 1)*splitter, dstWidth, dstHeight - (jobs - 1)*splitter);
OperatorWrapper wrapper(applicator);
QtConcurrent::blockingMap(rects, wrapper);
}
else {
QRect rect(0, 0, dstWidth, dstHeight);
applicator->process(rect);
}
}
void KisAutoBrush::toXML(QDomDocument& doc, QDomElement& e) const
{
QDomElement shapeElt = doc.createElement("MaskGenerator");
d->shape->toXML(doc, shapeElt);
e.appendChild(shapeElt);
e.setAttribute("type", "auto_brush");
e.setAttribute("spacing", QString::number(spacing()));
e.setAttribute("useAutoSpacing", QString::number(autoSpacingActive()));
e.setAttribute("autoSpacingCoeff", QString::number(autoSpacingCoeff()));
e.setAttribute("angle", QString::number(KisBrush::angle()));
e.setAttribute("randomness", QString::number(d->randomness));
e.setAttribute("density", QString::number(d->density));
KisBrush::toXML(doc, e);
}
QImage KisAutoBrush::createBrushPreview()
{
srand(0);
srand48(0);
int width = maskWidth(1.0, 0.0, 0.0, 0.0, KisPaintInformation());
int height = maskHeight(1.0, 0.0, 0.0, 0.0, KisPaintInformation());
KisPaintInformation info(QPointF(width * 0.5, height * 0.5), 0.5, 0, 0, angle(), 0, 0, 0, 0);
KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
fdev->setRect(QRect(0, 0, width, height));
fdev->initialize();
mask(fdev, KoColor(Qt::black, fdev->colorSpace()), 1.0, 1.0, 0.0, info);
return fdev->convertToQImage(0);
}
const KisMaskGenerator* KisAutoBrush::maskGenerator() const
{
return d->shape;
}
qreal KisAutoBrush::density() const
{
return d->density;
}
qreal KisAutoBrush::randomness() const
{
return d->randomness;
}
QPainterPath KisAutoBrush::outline() const
{
bool simpleOutline = (d->density < 1.0);
if (simpleOutline) {
QPainterPath path;
QRectF brushBoundingbox(0, 0, width(), height());
if (maskGenerator()->type() == KisMaskGenerator::CIRCLE) {
path.addEllipse(brushBoundingbox);
}
else { // if (maskGenerator()->type() == KisMaskGenerator::RECTANGLE)
path.addRect(brushBoundingbox);
}
return path;
}
return KisBrush::boundary()->path();
}
diff --git a/krita/libbrush/kis_gbr_brush.cpp b/krita/libbrush/kis_gbr_brush.cpp
index c502551254b..2a1f8361c3e 100644
--- a/krita/libbrush/kis_gbr_brush.cpp
+++ b/krita/libbrush/kis_gbr_brush.cpp
@@ -1,524 +1,524 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <netinet/in.h> // htonl
#include "kis_gbr_brush.h"
#include "kis_brush.h"
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPoint>
#include <kis_debug.h>
#include <klocale.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include "kis_datamanager.h"
#include "kis_paint_device.h"
#include "kis_global.h"
#include "kis_image.h"
struct GimpBrushV1Header {
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 width; /* width of brush */
quint32 height; /* height of brush */
quint32 bytes; /* depth of brush in bytes */
};
/// All fields are in MSB on disk!
struct GimpBrushHeader {
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 width; /* width of brush */
quint32 height; /* height of brush */
quint32 bytes; /* depth of brush in bytes */
/* The following are only defined in version 2 */
quint32 magic_number; /* GIMP brush magic number */
quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */
};
// Needed, or the GIMP won't open it!
quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0);
struct KisGbrBrush::Private {
QByteArray data;
bool ownData; /* seems to indicate that @ref data is owned by the brush, but in Qt4.x this is already guaranteed... so in reality it seems more to indicate whether the data is loaded from file (ownData = true) or memory (ownData = false) */
bool useColorAsMask;
quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */
quint32 version; /* brush file version # */
quint32 bytes; /* depth of brush in bytes */
quint32 magic_number; /* GIMP brush magic number */
};
#define DEFAULT_SPACING 0.25
KisGbrBrush::KisGbrBrush(const QString& filename)
: KisBrush(filename)
, d(new Private)
{
d->ownData = true;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
}
KisGbrBrush::KisGbrBrush(const QString& filename,
const QByteArray& data,
qint32 & dataPos)
: KisBrush(filename)
, d(new Private)
{
d->ownData = false;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos);
init();
d->data.clear();
dataPos += d->header_size + (width() * height() * d->bytes);
}
KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h)
: KisBrush()
, d(new Private)
{
d->ownData = true;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
initFromPaintDev(image, x, y, w, h);
}
KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name)
: KisBrush()
, d(new Private)
{
d->ownData = false;
d->useColorAsMask = false;
setHasColor(false);
setSpacing(DEFAULT_SPACING);
setBrushTipImage(image);
setName(name);
}
KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs)
: KisBrush(rhs)
, d(new Private(*rhs.d))
{
setName(rhs.name());
d->data = QByteArray();
setValid(rhs.valid());
}
KisGbrBrush::~KisGbrBrush()
{
delete d;
}
bool KisGbrBrush::load()
{
QFile file(filename());
if (file.size() == 0) return false;
file.open(QIODevice::ReadOnly);
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KisGbrBrush::loadFromDevice(QIODevice *dev)
{
if (d->ownData) {
d->data = dev->readAll();
}
return init();
}
bool KisGbrBrush::init()
{
GimpBrushHeader bh;
if (sizeof(GimpBrushHeader) > (uint)d->data.size()) {
return false;
}
memcpy(&bh, d->data, sizeof(GimpBrushHeader));
bh.header_size = ntohl(bh.header_size);
d->header_size = bh.header_size;
bh.version = ntohl(bh.version);
d->version = bh.version;
bh.width = ntohl(bh.width);
bh.height = ntohl(bh.height);
bh.bytes = ntohl(bh.bytes);
d->bytes = bh.bytes;
bh.magic_number = ntohl(bh.magic_number);
d->magic_number = bh.magic_number;
if (bh.version == 1) {
// No spacing in version 1 files so use Gimp default
bh.spacing = static_cast<int>(DEFAULT_SPACING * 100);
}
else {
bh.spacing = ntohl(bh.spacing);
if (bh.spacing > 1000) {
return false;
}
}
setSpacing(bh.spacing / 100.0);
if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) {
return false;
}
QString name;
if (bh.version == 1) {
// Version 1 has no magic number or spacing, so the name
// is at a different offset. Character encoding is undefined.
const char *text = d->data.constData() + sizeof(GimpBrushV1Header);
name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1);
}
else {
// ### Version = 3->cinepaint; may be float16 data!
// Version >=2: UTF-8 encoding is used
name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader),
bh.header_size - sizeof(GimpBrushHeader) - 1);
}
setName(name);
if (bh.width == 0 || bh.height == 0) {
return false;
}
QImage::Format imageFormat;
if (bh.bytes == 1) {
imageFormat = QImage::Format_Indexed8;
} else {
imageFormat = QImage::Format_ARGB32;
}
QImage image(QImage(bh.width, bh.height, imageFormat));
if (image.isNull()) {
return false;
}
qint32 k = bh.header_size;
if (bh.bytes == 1) {
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i));
image.setColorTable(table);
// Grayscale
if (static_cast<qint32>(k + bh.width * bh.height) > d->data.size()) {
return false;
}
setHasColor(false);
for (quint32 y = 0; y < bh.height; y++) {
uchar *pixel = reinterpret_cast<uchar *>(image.scanLine(y));
for (quint32 x = 0; x < bh.width; x++, k++) {
qint32 val = 255 - static_cast<uchar>(d->data[k]);
*pixel = val;
++pixel;
}
}
} else if (bh.bytes == 4) {
// RGBA
if (static_cast<qint32>(k + (bh.width * bh.height * 4)) > d->data.size()) {
return false;
}
setHasColor(true);
for (quint32 y = 0; y < bh.height; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
for (quint32 x = 0; x < bh.width; x++, k += 4) {
*pixel = qRgba(d->data[k], d->data[k + 1], d->data[k + 2], d->data[k + 3]);
++pixel;
}
}
}
else {
- qWarning() << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported";
+ warnKrita << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported";
return false;
}
setWidth(image.width());
setHeight(image.height());
if (d->ownData) {
d->data.resize(0); // Save some memory, we're using enough of it as it is.
}
setValid(image.width() != 0 && image.height() != 0);
setBrushTipImage(image);
return true;
}
bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h)
{
// Forcefully convert to RGBA8
// XXX profile and exposure?
setBrushTipImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags));
setName(image->objectName());
setHasColor(true);
return true;
}
bool KisGbrBrush::save()
{
QFile file(filename());
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
bool ok = saveToDevice(&file);
file.close();
return ok;
}
bool KisGbrBrush::saveToDevice(QIODevice* dev) const
{
GimpBrushHeader bh;
QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8
char const* name = utf8Name.data();
int nameLength = qstrlen(name);
int wrote;
bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength + 1);
bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff
bh.width = htonl(width());
bh.height = htonl(height());
// Hardcoded, 4 bytes RGBA or 1 byte GREY
if (!hasColor()) {
bh.bytes = htonl(1);
}
else {
bh.bytes = htonl(4);
}
bh.magic_number = htonl(GimpV2BrushMagic);
bh.spacing = htonl(static_cast<quint32>(spacing() * 100.0));
// Write header: first bh, then the name
QByteArray bytes = QByteArray::fromRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
wrote = dev->write(bytes);
bytes.clear();
if (wrote == -1) {
return false;
}
wrote = dev->write(name, nameLength + 1);
if (wrote == -1) {
return false;
}
int k = 0;
QImage image = brushTipImage();
if (!hasColor()) {
bytes.resize(width() * height());
for (qint32 y = 0; y < height(); y++) {
for (qint32 x = 0; x < width(); x++) {
QRgb c = image.pixel(x, y);
bytes[k++] = static_cast<char>(255 - qRed(c)); // red == blue == green
}
}
} else {
bytes.resize(width() * height() * 4);
for (qint32 y = 0; y < height(); y++) {
for (qint32 x = 0; x < width(); x++) {
// order for gimp brushes, v2 is: RGBA
QRgb pixel = image.pixel(x, y);
bytes[k++] = static_cast<char>(qRed(pixel));
bytes[k++] = static_cast<char>(qGreen(pixel));
bytes[k++] = static_cast<char>(qBlue(pixel));
bytes[k++] = static_cast<char>(qAlpha(pixel));
}
}
}
wrote = dev->write(bytes);
if (wrote == -1) {
return false;
}
KoResource::saveToDevice(dev);
return true;
}
QImage KisGbrBrush::brushTipImage() const
{
QImage image = KisBrush::brushTipImage();
if (hasColor() && useColorAsMask()) {
for (int y = 0; y < image.height(); y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++) {
QRgb c = pixel[x];
int a = qGray(c);
pixel[x] = qRgba(a, a, a, qAlpha(c));
}
}
}
return image;
}
enumBrushType KisGbrBrush::brushType() const
{
return !hasColor() || useColorAsMask() ? MASK : IMAGE;
}
void KisGbrBrush::setBrushType(enumBrushType type)
{
Q_UNUSED(type);
qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush");
}
void KisGbrBrush::setBrushTipImage(const QImage& image)
{
KisBrush::setBrushTipImage(image);
setValid(true);
}
/*QImage KisGbrBrush::outline(double pressure) {
KisLayerSP layer = image(KoColorSpaceRegistry::instance()->colorSpace("RGBA",0),
KisPaintInformation(pressure));
KisBoundary bounds(layer.data());
int w = maskWidth(pressure);
int h = maskHeight(pressure);
bounds.generateBoundary(w, h);
QPixmap pix(bounds.pixmap(w, h));
QImage result;
result = pix;
return result;
}*/
void KisGbrBrush::makeMaskImage()
{
if (!hasColor()) {
return;
}
QImage brushTip = brushTipImage();
if (brushTip.width() == width() && brushTip.height() == height()) {
int imageWidth = width();
int imageHeight = height();
QImage image(imageWidth, imageHeight, QImage::Format_Indexed8);
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) {
table.append(qRgb(i, i, i));
}
image.setColorTable(table);
for (int y = 0; y < imageHeight; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(brushTip.scanLine(y));
uchar * dstPixel = image.scanLine(y);
for (int x = 0; x < imageWidth; x++) {
QRgb c = pixel[x];
float alpha = qAlpha(c) / 255.0f;
// linear interpolation with maximum gray value which is transparent in the mask
//int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255);
// single multiplication version
int a = 255 + alpha * (qGray(c) - 255);
dstPixel[x] = (uchar)a;
}
}
setBrushTipImage(image);
}
setHasColor(false);
setUseColorAsMask(false);
resetBoundary();
clearBrushPyramid();
}
KisGbrBrush* KisGbrBrush::clone() const
{
return new KisGbrBrush(*this);
}
void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const
{
predefinedBrushToXML("gbr_brush", e);
e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask()));
KisBrush::toXML(d, e);
}
void KisGbrBrush::setUseColorAsMask(bool useColorAsMask)
{
/**
* WARNING: There is a problem in the brush server, since it
* returns not copies of brushes, but direct pointers to them. It
* means that the brushes are shared among all the currently
* present paintops, which might be a problem for e.g. Multihand
* Brush Tool.
*
* Right now, all the instances of Multihand Brush Tool share the
* same brush, so there is no problem in this sharing, unless we
* reset the internal state of the brush on our way.
*/
if (useColorAsMask != d->useColorAsMask) {
d->useColorAsMask = useColorAsMask;
resetBoundary();
clearBrushPyramid();
}
}
bool KisGbrBrush::useColorAsMask() const
{
return d->useColorAsMask;
}
QString KisGbrBrush::defaultFileExtension() const
{
return QString(".gbr");
}
diff --git a/krita/libbrush/kis_png_brush.cpp b/krita/libbrush/kis_png_brush.cpp
index 87ff5bc03c1..e47f2551a36 100644
--- a/krita/libbrush/kis_png_brush.cpp
+++ b/krita/libbrush/kis_png_brush.cpp
@@ -1,128 +1,128 @@
/*
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_png_brush.h"
#include <QDomElement>
#include <QFileInfo>
#include <QImageReader>
#include <QByteArray>
#include <QBuffer>
KisPngBrush::KisPngBrush(const QString& filename)
: KisBrush(filename)
{
setBrushType(INVALID);
setSpacing(0.25);
setHasColor(false);
}
bool KisPngBrush::load()
{
QFile f(filename());
if (f.size() == 0) return false;
if (!f.exists()) return false;
if (!f.open(QIODevice::ReadOnly)) {
warnKrita << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&f);
f.close();
return res;
}
bool KisPngBrush::loadFromDevice(QIODevice *dev)
{
// Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
// fails with "libpng error: IDAT: CRC error"
QByteArray data = dev->readAll();
QBuffer buf(&data);
buf.open(QIODevice::ReadOnly);
QImageReader reader(&buf, "PNG");
if (!reader.canRead()) {
setValid(false);
return false;
}
if (reader.textKeys().contains("brush_spacing")) {
setSpacing(reader.text("brush_spacing").toDouble());
}
if (reader.textKeys().contains("brush_name")) {
setName(reader.text("brush_name"));
}
else {
QFileInfo info(filename());
setName(info.baseName());
}
QImage image = reader.read();
if (image.isNull()) {
- kWarning() << "Could not read brush" << filename() << ". Error:" << reader.errorString();
+ dbgKrita << "Could not read brush" << filename() << ". Error:" << reader.errorString();
setValid(false);
return false;
}
setBrushTipImage(image);
setValid(!brushTipImage().isNull());
if (brushTipImage().isGrayscale()) {
setBrushType(MASK);
setHasColor(false);
}
else {
setBrushType(IMAGE);
setHasColor(true);
}
setWidth(brushTipImage().width());
setHeight(brushTipImage().height());
return !brushTipImage().isNull();
}
bool KisPngBrush::save()
{
QFile f(filename());
if (!f.open(QFile::WriteOnly)) return false;
bool res = saveToDevice(&f);
f.close();
return res;
}
bool KisPngBrush::saveToDevice(QIODevice *dev) const
{
if(brushTipImage().save(dev, "PNG")) {
KoResource::saveToDevice(dev);
return true;
}
return false;
}
QString KisPngBrush::defaultFileExtension() const
{
return QString(".png");
}
void KisPngBrush::toXML(QDomDocument& d, QDomElement& e) const
{
predefinedBrushToXML("png_brush", e);
KisBrush::toXML(d, e);
}
diff --git a/krita/libbrush/kis_qimage_pyramid.cpp b/krita/libbrush/kis_qimage_pyramid.cpp
index 48be9fc9893..52fba9da707 100644
--- a/krita/libbrush/kis_qimage_pyramid.cpp
+++ b/krita/libbrush/kis_qimage_pyramid.cpp
@@ -1,311 +1,311 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_qimage_pyramid.h"
#include <limits>
#include <QPainter>
#include <kis_debug.h>
#define MIPMAP_SIZE_THRESHOLD 512
#define MAX_MIPMAP_SCALE 8.0
#define QPAINTER_WORKAROUND_BORDER 1
KisQImagePyramid::KisQImagePyramid(const QImage &baseImage)
{
Q_ASSERT(!baseImage.isNull());
m_originalSize = baseImage.size();
qreal scale = MAX_MIPMAP_SCALE;
while (scale > 1.0) {
QSize scaledSize = m_originalSize * scale;
if (scaledSize.width() <= MIPMAP_SIZE_THRESHOLD ||
scaledSize.height() <= MIPMAP_SIZE_THRESHOLD) {
if (m_levels.isEmpty()) {
m_baseScale = scale;
}
appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
scale *= 0.5;
}
if (m_levels.isEmpty()) {
m_baseScale = 1.0;
}
appendPyramidLevel(baseImage);
scale = 0.5;
while (true) {
QSize scaledSize = m_originalSize * scale;
if (scaledSize.width() == 0 ||
scaledSize.height() == 0) break;
appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
scale *= 0.5;
}
}
KisQImagePyramid::~KisQImagePyramid()
{
}
int KisQImagePyramid::findNearestLevel(qreal scale, qreal *baseScale)
{
const qreal scale_epsilon = 1e-6;
qreal levelScale = m_baseScale;
int level = 0;
int lastLevel = m_levels.size() - 1;
while ((0.5 * levelScale > scale ||
qAbs(0.5 * levelScale - scale) < scale_epsilon) &&
level < lastLevel) {
levelScale *= 0.5;
level++;
}
*baseScale = levelScale;
return level;
}
inline QRect roundRect(const QRectF &rc)
{
/**
* This is an analog of toAlignedRect() with the only difference
* that it ensures the rect position will never be below zero.
*
* Warning: be *very* careful with using bottom()/right() values
* of a pure QRect (we don't use it here for the dangers
* it can lead to).
*/
QRectF rect(rc);
KIS_ASSERT_RECOVER_NOOP(rect.x() > -1e-6);
KIS_ASSERT_RECOVER_NOOP(rect.y() > -1e-6);
if (rect.x() < 0.0) {
rect.setLeft(0.0);
}
if (rect.y() < 0.0) {
rect.setTop(0.0);
}
return rect.toAlignedRect();
}
QTransform baseBrushTransform(qreal scaleX, qreal scaleY,
qreal rotation,
qreal subPixelX, qreal subPixelY,
const QRectF &baseBounds)
{
QTransform transform;
if (!qFuzzyCompare(rotation, 0)) {
QTransform rotationTransform;
rotationTransform.rotateRadians(rotation);
QRectF rotatedBounds = rotationTransform.mapRect(baseBounds);
transform = rotationTransform *
QTransform::fromTranslate(-rotatedBounds.x(), -rotatedBounds.y());
}
return transform *
QTransform::fromScale(scaleX, scaleY) *
QTransform::fromTranslate(subPixelX, subPixelY);
}
void KisQImagePyramid::calculateParams(qreal scale, qreal rotation,
qreal subPixelX, qreal subPixelY,
const QSize &originalSize,
QTransform *outputTransform, QSize *outputSize)
{
calculateParams(scale, rotation,
subPixelX, subPixelY,
originalSize, 1.0, originalSize,
outputTransform, outputSize);
}
void KisQImagePyramid::calculateParams(qreal scale, qreal rotation,
qreal subPixelX, qreal subPixelY,
const QSize &originalSize,
qreal baseScale, const QSize &baseSize,
QTransform *outputTransform, QSize *outputSize)
{
Q_UNUSED(baseScale);
QRectF originalBounds = QRectF(QPointF(), originalSize);
QTransform originalTransform =
baseBrushTransform(scale, scale,
rotation,
subPixelX, subPixelY,
originalBounds);
qreal realBaseScaleX = qreal(baseSize.width()) / originalSize.width();
qreal realBaseScaleY = qreal(baseSize.height()) / originalSize.height();
qreal scaleX = scale / realBaseScaleX;
qreal scaleY = scale / realBaseScaleY;
QRectF baseBounds = QRectF(QPointF(), baseSize);
QTransform transform =
baseBrushTransform(scaleX, scaleY,
rotation,
subPixelX, subPixelY,
baseBounds);
QRect expectedDstRect = roundRect(originalTransform.mapRect(originalBounds));
#if 0 // Only enable when debugging; users shouldn't see this warning
{
QRect testingRect = roundRect(transform.mapRect(baseBounds));
if (testingRect != expectedDstRect) {
- qWarning() << "WARNING: expected and real dab rects do not coincide!";
- qWarning() << " expected rect:" << expectedDstRect;
- qWarning() << " real rect: " << testingRect;
+ warnKrita << "WARNING: expected and real dab rects do not coincide!";
+ warnKrita << " expected rect:" << expectedDstRect;
+ warnKrita << " real rect: " << testingRect;
}
}
#endif
KIS_ASSERT_RECOVER_NOOP(expectedDstRect.x() >= 0);
KIS_ASSERT_RECOVER_NOOP(expectedDstRect.y() >= 0);
int width = expectedDstRect.x() + expectedDstRect.width();
int height = expectedDstRect.y() + expectedDstRect.height();
// we should not return invalid image, so adjust the image to be
// at least 1 px in size.
width = qMax(1, width);
height = qMax(1, height);
*outputTransform = transform;
*outputSize = QSize(width, height);
}
QSize KisQImagePyramid::imageSize(const QSize &originalSize,
qreal scale, qreal rotation,
qreal subPixelX, qreal subPixelY)
{
QTransform transform;
QSize dstSize;
calculateParams(scale, rotation, subPixelX, subPixelY,
originalSize,
&transform, &dstSize);
return dstSize;
}
QSizeF KisQImagePyramid::characteristicSize(const QSize &originalSize,
qreal scale, qreal rotation)
{
QRectF originalRect(QPointF(), originalSize);
QTransform transform = baseBrushTransform(scale, scale,
rotation,
0.0, 0.0,
originalRect);
return transform.mapRect(originalRect).size();
}
void KisQImagePyramid::appendPyramidLevel(const QImage &image)
{
/**
* QPainter has a bug: when doing a transformation it decides that
* all the pixels outside of the image (source rect) are equal to
* the border pixels (CLAMP in terms of openGL). This means that
* there will be no smooth scaling on the border of the image when
* it is rotated. To workaround this bug we need to add one pixel
* wide border to the image, so that it transforms smoothly.
*
* See a unittest in: KisBrushTest::testQPainterTransformationBorder
*/
QSize levelSize = image.size();
QImage tmp = image.convertToFormat(QImage::Format_ARGB32);
tmp = tmp.copy(-QPAINTER_WORKAROUND_BORDER,
-QPAINTER_WORKAROUND_BORDER,
image.width() + 2 * QPAINTER_WORKAROUND_BORDER,
image.height() + 2 * QPAINTER_WORKAROUND_BORDER);
m_levels.append(PyramidLevel(tmp, levelSize));
}
QImage KisQImagePyramid::createImage(qreal scale, qreal rotation,
qreal subPixelX, qreal subPixelY)
{
qreal baseScale = -1.0;
int level = findNearestLevel(scale, &baseScale);
const QImage &srcImage = m_levels[level].image;
QTransform transform;
QSize dstSize;
calculateParams(scale, rotation, subPixelX, subPixelY,
m_originalSize, baseScale, m_levels[level].size,
&transform, &dstSize);
if (transform.isIdentity() &&
srcImage.format() == QImage::Format_ARGB32) {
return srcImage.copy(QPAINTER_WORKAROUND_BORDER,
QPAINTER_WORKAROUND_BORDER,
srcImage.width() - 2 * QPAINTER_WORKAROUND_BORDER,
srcImage.height() - 2 * QPAINTER_WORKAROUND_BORDER);
}
QImage dstImage(dstSize, QImage::Format_ARGB32);
dstImage.fill(0);
/**
* QPainter has one more bug: when a QTransform is TxTranslate, it
* does wrong sampling (probably, Nearest Neighbour) even though
* we tell it directly that we need SmoothPixmapTransform.
*
* So here is a workaround: we set a negligible scale to convince
* Qt we use a non-only-translating transform.
*/
while (transform.type() == QTransform::TxTranslate) {
const qreal scale = transform.m11();
const qreal fakeScale = scale - 10 * std::numeric_limits<qreal>::epsilon();
transform *= QTransform::fromScale(fakeScale, fakeScale);
}
QPainter gc(&dstImage);
gc.setTransform(
QTransform::fromTranslate(-QPAINTER_WORKAROUND_BORDER,
-QPAINTER_WORKAROUND_BORDER) * transform);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QPointF(), srcImage);
gc.end();
return dstImage;
}
diff --git a/krita/libbrush/kis_text_brush.cpp b/krita/libbrush/kis_text_brush.cpp
index 045c45beb2d..e8e4afec95d 100644
--- a/krita/libbrush/kis_text_brush.cpp
+++ b/krita/libbrush/kis_text_brush.cpp
@@ -1,285 +1,285 @@
/*
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_text_brush.h"
#include <QDomDocument>
#include <QDomElement>
#include <QFontMetrics>
#include <QPainter>
#include "kis_gbr_brush.h"
#include "kis_brushes_pipe.h"
#include <kis_threaded_text_rendering_workaround.h>
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
#include <QApplication>
#include <QWidget>
#include <QThread>
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
class KisTextBrushesPipe : public KisBrushesPipe<KisGbrBrush>
{
public:
KisTextBrushesPipe() {
m_charIndex = 0;
m_currentBrushIndex = 0;
}
KisTextBrushesPipe(const KisTextBrushesPipe &rhs)
: KisBrushesPipe<KisGbrBrush>(rhs) {
m_brushesMap.clear();
QMapIterator<QChar, KisGbrBrush*> iter(rhs.m_brushesMap);
while (iter.hasNext()) {
iter.next();
m_brushesMap.insert(iter.key(), iter.value());
}
}
void setText(const QString &text, const QFont &font) {
m_text = text;
m_charIndex = 0;
clear();
for (int i = 0; i < m_text.length(); i++) {
QChar letter = m_text.at(i);
QImage image = renderChar(letter, font);
KisGbrBrush *brush = new KisGbrBrush(image, letter);
brush->setSpacing(0.1); // support for letter spacing?
brush->makeMaskImage();
m_brushesMap.insert(letter, brush);
KisBrushesPipe<KisGbrBrush>::addBrush(brush);
}
}
static QImage renderChar(const QString& text, const QFont &font) {
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
QWidget *focusWidget = qApp->focusWidget();
if (focusWidget) {
QThread *guiThread = focusWidget->thread();
if (guiThread != QThread::currentThread()) {
- qWarning() << "WARNING: Rendering text in non-GUI thread!"
+ warnKrita << "WARNING: Rendering text in non-GUI thread!"
<< "That may lead to hangups and crashes on some"
<< "versions of X11/Qt!";
}
}
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
QFontMetrics metric(font);
QRect rect = metric.boundingRect(text);
if (rect.isEmpty()) {
rect = QRect(0, 0, 1, 1); // paint at least something
}
QRect paintingRect = rect.translated(-rect.x(), -rect.y());
QImage renderedChar(paintingRect.size(), QImage::Format_ARGB32);
QPainter p;
p.begin(&renderedChar);
p.setFont(font);
p.fillRect(paintingRect, Qt::white);
p.setPen(Qt::black);
p.drawText(-rect.x(), -rect.y(), text);
p.end();
return renderedChar;
}
void clear() {
m_brushesMap.clear();
KisBrushesPipe<KisGbrBrush>::clear();
}
KisGbrBrush* firstBrush() const {
Q_ASSERT(m_text.size() > 0);
Q_ASSERT(m_brushesMap.size() > 0);
return m_brushesMap.value(m_text.at(0));
}
protected:
int chooseNextBrush(const KisPaintInformation& info) const {
Q_UNUSED(info);
return m_currentBrushIndex;
}
void updateBrushIndexes() {
m_charIndex++;
if (m_charIndex >= m_text.size()) {
m_charIndex = 0;
}
QChar letter = m_text.at(m_charIndex);
Q_ASSERT(m_brushesMap.contains(letter));
m_currentBrushIndex = m_brushes.indexOf(m_brushesMap.value(letter));
}
private:
QMap<QChar, KisGbrBrush*> m_brushesMap;
QString m_text;
int m_charIndex;
int m_currentBrushIndex;
};
KisTextBrush::KisTextBrush()
: m_brushesPipe(new KisTextBrushesPipe())
{
setPipeMode(false);
}
KisTextBrush::KisTextBrush(const KisTextBrush &rhs)
: KisBrush(rhs),
m_brushesPipe(new KisTextBrushesPipe(*rhs.m_brushesPipe))
{
}
KisTextBrush::~KisTextBrush()
{
delete m_brushesPipe;
}
void KisTextBrush::setPipeMode(bool pipe)
{
setBrushType(pipe ? PIPE_MASK : MASK);
}
bool KisTextBrush::pipeMode() const
{
return brushType() == PIPE_MASK;
}
void KisTextBrush::setText(const QString& txt)
{
m_text = txt;
}
QString KisTextBrush::text(void) const
{
return m_text;
}
void KisTextBrush::setFont(const QFont& font)
{
m_font = font;
}
QFont KisTextBrush::font()
{
return m_font;
}
void KisTextBrush::notifyCachedDabPainted()
{
m_brushesPipe->notifyCachedDabPainted();
}
void KisTextBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY, qreal softnessFactor) const
{
if (brushType() == MASK) {
KisBrush::generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor);
}
else { /* if (brushType() == PIPE_MASK)*/
m_brushesPipe->generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, scaleX, scaleY, angle, info, subPixelX, subPixelY, softnessFactor);
}
}
KisFixedPaintDeviceSP KisTextBrush::paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) const
{
if (brushType() == MASK) {
return KisBrush::paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY);
}
else { /* if (brushType() == PIPE_MASK)*/
return m_brushesPipe->paintDevice(colorSpace, scale, angle, info, subPixelX, subPixelY);
}
}
void KisTextBrush::toXML(QDomDocument& doc, QDomElement& e) const
{
Q_UNUSED(doc);
e.setAttribute("type", "kis_text_brush");
e.setAttribute("spacing", spacing());
e.setAttribute("text", m_text);
e.setAttribute("font", m_font.toString());
e.setAttribute("pipe", (brushType() == PIPE_MASK) ? "true" : "false");
KisBrush::toXML(doc, e);
}
void KisTextBrush::updateBrush()
{
Q_ASSERT((brushType() == PIPE_MASK) || (brushType() == MASK));
if (brushType() == PIPE_MASK) {
m_brushesPipe->setText(m_text, m_font);
setBrushTipImage(m_brushesPipe->firstBrush()->brushTipImage());
}
else { /* if (brushType() == MASK)*/
setBrushTipImage(KisTextBrushesPipe::renderChar(m_text, m_font));
}
resetBoundary();
setValid(true);
}
quint32 KisTextBrush::brushIndex(const KisPaintInformation& info) const
{
return brushType() == MASK ? 0 : 1 + m_brushesPipe->brushIndex(info);
}
qint32 KisTextBrush::maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
return brushType() == MASK ?
KisBrush::maskWidth(scale, angle, subPixelX, subPixelY, info) :
m_brushesPipe->maskWidth(scale, angle, subPixelX, subPixelY, info);
}
qint32 KisTextBrush::maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const
{
return brushType() == MASK ?
KisBrush::maskHeight(scale, angle, subPixelX, subPixelY, info) :
m_brushesPipe->maskHeight(scale, angle, subPixelX, subPixelY, info);
}
void KisTextBrush::setAngle(qreal _angle)
{
KisBrush::setAngle(_angle);
m_brushesPipe->setAngle(_angle);
}
void KisTextBrush::setScale(qreal _scale)
{
KisBrush::setScale(_scale);
m_brushesPipe->setScale(_scale);
}
void KisTextBrush::setSpacing(double _spacing)
{
KisBrush::setSpacing(_spacing);
m_brushesPipe->setSpacing(_spacing);
}
KisBrush* KisTextBrush::clone() const
{
return new KisTextBrush(*this);
}
diff --git a/krita/libbrush/tests/kis_imagepipe_brush_test.cpp b/krita/libbrush/tests/kis_imagepipe_brush_test.cpp
index 94187e07123..312302c812d 100644
--- a/krita/libbrush/tests/kis_imagepipe_brush_test.cpp
+++ b/krita/libbrush/tests/kis_imagepipe_brush_test.cpp
@@ -1,277 +1,277 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_imagepipe_brush_test.h"
#include <qtest_kde.h>
#include <QPainter>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <kis_fixed_paint_device.h>
#include <kis_paint_information.h>
#include "kis_imagepipe_brush.h"
#include <kis_paint_device.h>
#include <kis_painter.h>
#define COMPARE_ALL(brush, method) \
foreach(KisGbrBrush *child, brush->testingGetBrushes()) { \
if(brush->method() != child->method()) { \
- qDebug() << "Failing method:" << #method \
+ dbgKrita << "Failing method:" << #method \
<< "brush index:" \
<< brush->testingGetBrushes().indexOf(child); \
QCOMPARE(brush->method(), child->method()); \
} \
}
inline void KisImagePipeBrushTest::checkConsistency(KisImagePipeBrush *brush)
{
qreal scale = 0.5; Q_UNUSED(scale);
KisGbrBrush *firstBrush = brush->testingGetBrushes().first();
/**
* This set of values is supposed to be constant, so
* it is just set to the corresponding values of the
* first brush
*/
QCOMPARE(brush->width(), firstBrush->width());
QCOMPARE(brush->height(), firstBrush->height());
QCOMPARE(brush->boundary(), firstBrush->boundary());
/**
* These values should be spread over the children brushes
*/
COMPARE_ALL(brush, maskAngle);
COMPARE_ALL(brush, scale);
COMPARE_ALL(brush, angle);
COMPARE_ALL(brush, spacing);
/**
* Check mask size values, they depend on current brush
*/
KisPaintInformation info;
KisBrush *oldBrush = brush->testingGetCurrentBrush(info);
QVERIFY(oldBrush);
qreal realScale = 1;
qreal realAngle = 0;
qreal subPixelX = 0;
qreal subPixelY = 0;
int maskWidth = brush->maskWidth(realScale, realAngle, subPixelX, subPixelY, info);
int maskHeight = brush->maskHeight(realScale, realAngle, subPixelX, subPixelY, info);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KisFixedPaintDeviceSP dev = brush->testingGetCurrentBrush(info)->paintDevice(cs, realScale, realAngle, info, subPixelX, subPixelY);
QCOMPARE(maskWidth, dev->bounds().width());
QCOMPARE(maskHeight, dev->bounds().height());
KisBrush *newBrush = brush->testingGetCurrentBrush(info);
QCOMPARE(oldBrush, newBrush);
}
void KisImagePipeBrushTest::testLoading()
{
KisImagePipeBrush *brush = new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih");
brush->load();
QVERIFY(brush->valid());
checkConsistency(brush);
delete brush;
}
void KisImagePipeBrushTest::testChangingBrushes()
{
KisImagePipeBrush *brush = new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih");
brush->load();
QVERIFY(brush->valid());
qreal rotation = 0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
for (int i = 0; i < 100; i++) {
checkConsistency(brush);
brush->testingSelectNextBrush(info);
}
delete brush;
}
void checkIncrementalPainting(KisBrush *brush, const QString &prefix)
{
qreal realScale = 1;
qreal realAngle = 0;
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor fillColor(Qt::red, cs);
KisFixedPaintDeviceSP fixedDab = new KisFixedPaintDevice(cs);
qreal rotation = 0;
qreal subPixelX = 0.0;
qreal subPixelY = 0.0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
for (int i = 0; i < 20; i++) {
int maskWidth = brush->maskWidth(realScale, realAngle, subPixelX, subPixelY, info);
int maskHeight = brush->maskHeight(realScale, realAngle, subPixelX, subPixelY, info);
QRect fillRect(0, 0, maskWidth, maskHeight);
fixedDab->setRect(fillRect);
fixedDab->initialize();
fixedDab->fill(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), fillColor.data());
brush->mask(fixedDab, realScale, realScale, realAngle, info);
QCOMPARE(fixedDab->bounds(), fillRect);
QImage result = fixedDab->convertToQImage(0);
result.save(QString("fixed_dab_%1_%2.png").arg(prefix).arg(i));
}
}
void KisImagePipeBrushTest::testSimpleDabApplication()
{
KisImagePipeBrush *brush = new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "C_Dirty_Spot.gih");
brush->load();
QVERIFY(brush->valid());
checkConsistency(brush);
checkIncrementalPainting(brush, "simple");
delete brush;
}
void KisImagePipeBrushTest::testColoredDab()
{
KisImagePipeBrush *brush = new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih");
brush->load();
QVERIFY(brush->valid());
checkConsistency(brush);
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_IMAGE);
// let it be the mask (should be revertible)
brush->setUseColorAsMask(true);
QCOMPARE(brush->useColorAsMask(), true);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_MASK);
// revert back
brush->setUseColorAsMask(false);
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), true);
QCOMPARE(brush->brushType(), PIPE_IMAGE);
// convert to the mask (irreversible)
brush->makeMaskImage();
QCOMPARE(brush->useColorAsMask(), false);
QCOMPARE(brush->hasColor(), false);
QCOMPARE(brush->brushType(), PIPE_MASK);
checkConsistency(brush);
delete brush;
}
void KisImagePipeBrushTest::testColoredDabWash()
{
KisImagePipeBrush *brush = new KisImagePipeBrush(QString(FILES_DATA_DIR) + QDir::separator() + "G_Sparks.gih");
brush->load();
QVERIFY(brush->valid());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
qreal rotation = 0;
KisPaintInformation info(QPointF(100.0, 100.0), 0.5, 0, 0, rotation);
KisPaintDeviceSP layer = new KisPaintDevice(cs);
KisPainter painter(layer);
painter.setCompositeOp(COMPOSITE_ALPHA_DARKEN);
const QVector<KisGbrBrush*> gbrs = brush->testingGetBrushes();
KisFixedPaintDeviceSP dab = gbrs.at(0)->paintDevice(cs, 2.0, 0.0, info);
painter.bltFixed(0, 0, dab, 0, 0, dab->bounds().width(), dab->bounds().height());
painter.bltFixed(80, 60, dab, 0, 0, dab->bounds().width(), dab->bounds().height());
painter.end();
QRect rc = layer->exactBounds();
QImage result = layer->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
#if 0
// if you want to see the result on white background, set #if 1
QImage bg(result.size(), result.format());
bg.fill(Qt::white);
QPainter qPainter(&bg);
qPainter.drawImage(0, 0, result);
result = bg;
#endif
result.save("z_spark_alpha_darken.png");
delete brush;
}
#include "kis_text_brush.h"
void KisImagePipeBrushTest::testTextBrushNoPipes()
{
KisTextBrush *brush = new KisTextBrush();
brush->setPipeMode(false);
brush->setFont(QApplication::font());
brush->setText("The_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog");
brush->updateBrush();
checkIncrementalPainting(brush, "text_no_incremental");
delete brush;
}
void KisImagePipeBrushTest::testTextBrushPiped()
{
KisTextBrush *brush = new KisTextBrush();
brush->setPipeMode(true);
brush->setFont(QApplication::font());
brush->setText("The_Quick_Brown_Fox_Jumps_Over_The_Lazy_Dog");
brush->updateBrush();
checkIncrementalPainting(brush, "text_incremental");
delete brush;
}
QTEST_KDEMAIN(KisImagePipeBrushTest, GUI)
diff --git a/krita/libcolor/CMakeLists.txt b/krita/libcolor/CMakeLists.txt
index 7b923d9901e..18b3ef0335d 100644
--- a/krita/libcolor/CMakeLists.txt
+++ b/krita/libcolor/CMakeLists.txt
@@ -1,23 +1,23 @@
if (UNIX AND NOT APPLE)
add_subdirectory(colord)
set(kritacolor_LIB_SRCS kis_color_manager.h linux/kis_color_manager.cpp)
set(kritacolor_EXTRA_LIBRARIES kritacolord)
elseif (MSVC)
set(kritacolor_LIB_SRCS kis_color_manager.h dummy/kis_color_manager.cpp ${CMAKE_CURRENT_BINARY_DIR}/kritacolor_export.h)
else ()
set(kritacolor_LIB_SRCS dummy/kis_color_manager.cpp)
endif ()
add_library(kritacolor SHARED ${kritacolor_LIB_SRCS} )
generate_export_header(kritacolor BASE_NAME kritacolor)
-target_link_libraries(kritacolor ${QT_QTCORE_LIBRARY} KF5::KDELibs4Support ${kritacolor_EXTRA_LIBRARIES})
+target_link_libraries(kritacolor kritaglobal ${QT_QTCORE_LIBRARY} KF5::KDELibs4Support ${kritacolor_EXTRA_LIBRARIES})
-target_link_libraries(kritacolor LINK_INTERFACE_LIBRARIES ${QT_QTCORE_LIBRARY})
+target_link_libraries(kritacolor LINK_INTERFACE_LIBRARIES kritaglobal ${QT_QTCORE_LIBRARY})
set_target_properties(kritacolor PROPERTIES
VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
install(TARGETS kritacolor ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/krita/libcolor/colord/CMakeLists.txt b/krita/libcolor/colord/CMakeLists.txt
index f1493308395..07eed779ef9 100644
--- a/krita/libcolor/colord/CMakeLists.txt
+++ b/krita/libcolor/colord/CMakeLists.txt
@@ -1,41 +1,41 @@
set(kritacolord_SRCS
KisColord.cpp
)
set(COLORD_INTERFACE_XML
org.freedesktop.ColorManager.xml
)
set(COLORD_DEVICE_INTERFACE_XML
org.freedesktop.ColorManager.Device.xml
)
set(COLORD_PROFILE_INTERFACE_XML
org.freedesktop.ColorManager.Profile.xml
)
set(COLORD_SENSOR_INTERFACE_XML
org.freedesktop.ColorManager.Sensor.xml
)
set_source_files_properties(${COLORD_INTERFACE_XML} PROPERTIES INCLUDE "dbus-types.h")
set_source_files_properties(${COLORD_INTERFACE_XML} PROPERTIES NO_NAMESPACE true)
set_source_files_properties(${COLORD_INTERFACE_XML} PROPERTIES CLASSNAME CdInterface)
qt4_add_dbus_interface(kritacolord_SRCS ${COLORD_INTERFACE_XML} CdInterface)
set_source_files_properties(${COLORD_DEVICE_INTERFACE_XML} PROPERTIES INCLUDE "dbus-types.h")
set_source_files_properties(${COLORD_DEVICE_INTERFACE_XML} PROPERTIES NO_NAMESPACE true)
set_source_files_properties(${COLORD_DEVICE_INTERFACE_XML} PROPERTIES CLASSNAME CdDeviceInterface)
qt4_add_dbus_interface(kritacolord_SRCS ${COLORD_DEVICE_INTERFACE_XML} CdDeviceInterface)
set_source_files_properties(${COLORD_PROFILE_INTERFACE_XML} PROPERTIES INCLUDE "dbus-types.h")
set_source_files_properties(${COLORD_PROFILE_INTERFACE_XML} PROPERTIES NO_NAMESPACE true)
set_source_files_properties(${COLORD_PROFILE_INTERFACE_XML} PROPERTIES CLASSNAME CdProfileInterface)
qt4_add_dbus_interface(kritacolord_SRCS ${COLORD_PROFILE_INTERFACE_XML} CdProfileInterface)
set_source_files_properties(${COLORD_SENSOR_INTERFACE_XML} PROPERTIES INCLUDE "dbus-types.h")
set_source_files_properties(${COLORD_SENSOR_INTERFACE_XML} PROPERTIES NO_NAMESPACE true)
set_source_files_properties(${COLORD_SENSOR_INTERFACE_XML} PROPERTIES CLASSNAME CdSensorInterface)
qt4_add_dbus_interface(kritacolord_SRCS ${COLORD_SENSOR_INTERFACE_XML} CdSensorInterface)
kde4_add_library(kritacolord SHARED ${kritacolord_SRCS})
-target_link_libraries(kritacolord ${KDE4_KDEUI_LIBS})
+target_link_libraries(kritacolord kritaglobal ${KDE4_KDEUI_LIBS})
install(TARGETS kritacolord DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/krita/libcolor/colord/KisColord.cpp b/krita/libcolor/colord/KisColord.cpp
index 83536846930..7ef33951bf2 100644
--- a/krita/libcolor/colord/KisColord.cpp
+++ b/krita/libcolor/colord/KisColord.cpp
@@ -1,285 +1,285 @@
/*
* Copyright (C) 2012 by Daniel Nicoletti <dantti12@gmail.com>
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisColord.h"
#include <klocale.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kglobal.h>
#include "CdInterface.h"
#include "CdProfileInterface.h"
#include "CdDeviceInterface.h"
struct Profile {
QString kind;
QString filename;
QString title;
qulonglong created;
QString colorspace;
};
struct Device {
~Device() {
qDeleteAll(profiles);
profiles.clear();
}
QString id;
QString kind;
QString model;
QString vendor;
QString colorspace;
QList<Profile*> profiles;
};
KisColord::KisColord(QObject *parent)
: QObject(parent)
{
- //qDebug() << "Creating KisColorD";
+ //dbgKrita << "Creating KisColorD";
m_cdInterface = new CdInterface(QLatin1String("org.freedesktop.ColorManager"),
QLatin1String("/org/freedesktop/ColorManager"),
QDBusConnection::systemBus(),
this);
// listen to colord for device events
connect(m_cdInterface, SIGNAL(DeviceAdded(QDBusObjectPath)),
this, SLOT(deviceAdded(QDBusObjectPath)));
connect(m_cdInterface, SIGNAL(DeviceRemoved(QDBusObjectPath)),
this, SLOT(deviceRemoved(QDBusObjectPath)));
connect(m_cdInterface, SIGNAL(DeviceChanged(QDBusObjectPath)),
this, SLOT(deviceChanged(QDBusObjectPath)));
// Ask for devices
QDBusPendingReply<QList<QDBusObjectPath> > async = m_cdInterface->GetDevices();
QDBusPendingCallWatcher *displayWatcher = new QDBusPendingCallWatcher(async, this);
connect(displayWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
this, SLOT(gotDevices(QDBusPendingCallWatcher*)));
// Make sure we know is colord is running
QDBusServiceWatcher *watcher = new QDBusServiceWatcher("org.freedesktop.ColorManager",
QDBusConnection::systemBus(),
QDBusServiceWatcher::WatchForOwnerChange,
this);
connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
this, SLOT(serviceOwnerChanged(QString,QString,QString)));
}
KisColord::~KisColord()
{
qDeleteAll(m_devices);
m_devices.clear();
}
QStringList KisColord::devices(const QString &type) const
{
QStringList res;
foreach(Device *dev, m_devices.values()) {
if (type == dev->kind) {
res << dev->id;
}
}
return res;
}
const QString KisColord::deviceName(const QString &id) const
{
QString res;
foreach(Device *dev, m_devices.values()) {
if (dev->id == id) {
res = dev->model + ", " + dev->vendor;
}
}
return res;
}
QByteArray KisColord::deviceProfile(const QString &id, int p)
{
QByteArray ba;
Device *dev = 0;
Profile *profile = 0;
foreach(Device *d, m_devices.values()) {
if (d->id == id) {
dev = d;
break;
}
}
if (dev->profiles.size() > 0) {
if (dev) {
if (dev->profiles.size() < p) {
profile = dev->profiles[p];
}
else {
profile = dev->profiles[0];
}
}
}
if (profile) {
- //qDebug() << "profile filename" << profile->filename;
+ //dbgKrita << "profile filename" << profile->filename;
QFile f(profile->filename);
if (f.open(QFile::ReadOnly)) {
ba = f.readAll();
}
else {
- kWarning() << "Could not load profile" << profile->title << profile->filename;
+ dbgKrita << "Could not load profile" << profile->title << profile->filename;
}
}
return ba;
}
void KisColord::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
{
Q_UNUSED(serviceName)
if (newOwner.isEmpty() || oldOwner != newOwner) {
// colord has quit or restarted
qDeleteAll(m_devices);
m_devices.clear();
}
emit changed();
}
void KisColord::gotDevices(QDBusPendingCallWatcher *call)
{
- //qDebug() << "Got devices!!!";
+ //dbgKrita << "Got devices!!!";
QDBusPendingReply<QList<QDBusObjectPath> > reply = *call;
if (reply.isError()) {
- kWarning() << "Unexpected message" << reply.error().message();
+ dbgKrita << "Unexpected message" << reply.error().message();
} else {
QList<QDBusObjectPath> devices = reply.argumentAt<0>();
foreach (const QDBusObjectPath &device, devices) {
deviceAdded(device, false);
}
emit changed();
}
- //qDebug() << "gotDevices" << m_devices.count();
+ //dbgKrita << "gotDevices" << m_devices.count();
call->deleteLater();
}
void KisColord::deviceChanged(const QDBusObjectPath &objectPath)
{
CdDeviceInterface device(QLatin1String("org.freedesktop.ColorManager"),
objectPath.path(),
QDBusConnection::systemBus());
if (!device.isValid()) {
return;
}
if (!m_devices.contains(objectPath)) {
- //qDebug() << "deviceChanged for an unknown device" << objectPath.path();
+ //dbgKrita << "deviceChanged for an unknown device" << objectPath.path();
deviceAdded(objectPath, false);
return;
}
QList<QDBusObjectPath> profiles = device.profiles();
Device *dev = m_devices[objectPath];
qDeleteAll(dev->profiles);
dev->profiles.clear();
addProfilesToDevice(dev, profiles);
- //qDebug() << "deviceChanged" << dev->id << "with" << profiles.size() << "profiles";
+ //dbgKrita << "deviceChanged" << dev->id << "with" << profiles.size() << "profiles";
emit changed(dev->id);
}
void KisColord::deviceAdded(const QDBusObjectPath &objectPath, bool emitChanged)
{
if (m_devices.contains(objectPath)) {
- //kWarning() << "Device is already on the list" << objectPath.path();
+ //dbgKrita << "Device is already on the list" << objectPath.path();
return;
}
CdDeviceInterface device(QLatin1String("org.freedesktop.ColorManager"),
objectPath.path(),
QDBusConnection::systemBus());
if (!device.isValid()) {
- kWarning() << "Got an invalid device" << objectPath.path();
+ dbgKrita << "Got an invalid device" << objectPath.path();
return;
}
Device *dev = new Device;
dev->id = device.deviceId();
dev->kind = device.kind();
dev->model = device.model();
dev->vendor = device.vendor();
dev->colorspace = device.colorspace();
m_devices[objectPath] = dev;
QList<QDBusObjectPath> profiles = device.profiles();
addProfilesToDevice(dev, profiles);
-// qDebug() << "deviceAdded" << dev->id
+// dbgKrita << "deviceAdded" << dev->id
// << dev->kind
// << dev->model
// << dev->vendor
// << "with" << profiles.size() << "profiles";
if (emitChanged) {
emit changed();
}
}
void KisColord::deviceRemoved(const QDBusObjectPath &objectPath)
{
if (m_devices.contains(objectPath)) {
delete m_devices.take(objectPath);
}
emit changed();
}
void KisColord::addProfilesToDevice(Device *dev, QList<QDBusObjectPath> profiles) const
{
foreach (const QDBusObjectPath &profileObjectPath, profiles) {
CdProfileInterface profile(QLatin1String("org.freedesktop.ColorManager"),
profileObjectPath.path(),
QDBusConnection::systemBus());
if (!profile.isValid()) {
return;
}
Profile *p = new Profile;
p->kind = profile.kind();
p->filename = profile.filename();
p->title = profile.title();
p->created = profile.created();
p->colorspace = profile.colorspace();
dev->profiles << p;
}
}
diff --git a/krita/libcolor/linux/kis_color_manager.cpp b/krita/libcolor/linux/kis_color_manager.cpp
index 09ddd3d77ab..5aa24fdd23a 100644
--- a/krita/libcolor/linux/kis_color_manager.cpp
+++ b/krita/libcolor/linux/kis_color_manager.cpp
@@ -1,79 +1,79 @@
/*
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kglobal.h>
#include "kis_color_manager.h"
#include "colord/KisColord.h"
-#include <QDebug>
+#include <kis_debug.h>
class KisColorManager::Private {
public:
Private(QObject *parent)
: colord(new KisColord(parent))
{}
KisColord *colord;
};
KisColorManager::KisColorManager()
: QObject()
, d(new Private(this))
{
- //qDebug() << "ColorManager started";
+ //dbgKrita << "ColorManager started";
connect(d->colord, SIGNAL(changed(QString)), this, SIGNAL(changed(QString)));
}
KisColorManager::~KisColorManager()
{
delete d;
}
QString KisColorManager::deviceName(const QString &id)
{
return d->colord->deviceName(id);
}
QStringList KisColorManager::devices(DeviceType type) const
{
switch (type) {
case screen:
return d->colord->devices("display");
case printer:
return d->colord->devices("printer");
case camera:
return d->colord->devices("camera");
case scanner:
return d->colord->devices("scanner");
};
return QStringList();
}
QByteArray KisColorManager::displayProfile(const QString &device, int profile) const
{
return d->colord->deviceProfile(device, profile);
}
KisColorManager *KisColorManager::instance()
{
K_GLOBAL_STATIC(KisColorManager, s_instance);
return s_instance;
}
diff --git a/krita/libglobal/CMakeLists.txt b/krita/libglobal/CMakeLists.txt
index 1ad5241a20b..8a22731c2fb 100644
--- a/krita/libglobal/CMakeLists.txt
+++ b/krita/libglobal/CMakeLists.txt
@@ -1,15 +1,20 @@
+include(CheckFunctionExists)
+check_function_exists(backtrace HAVE_BACKTRACE)
+configure_file(config-debug.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-debug.h)
+
set(kritaglobal_LIB_SRCS
kis_assert.cpp
+ kis_debug.cpp
)
kde4_add_library(kritaglobal SHARED ${kritaglobal_LIB_SRCS} )
target_link_libraries(kritaglobal Qt5::Concurrent ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${KDE4_KDEUI_LIBS})
target_link_libraries(kritaglobal LINK_INTERFACE_LIBRARIES Qt5::Concurrent ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
set_target_properties(kritaglobal PROPERTIES
VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
install(TARGETS kritaglobal ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/krita/libglobal/config-debug.h.cmake b/krita/libglobal/config-debug.h.cmake
new file mode 100644
index 00000000000..49dda07ed05
--- /dev/null
+++ b/krita/libglobal/config-debug.h.cmake
@@ -0,0 +1 @@
+#cmakedefine01 HAVE_BACKTRACE
diff --git a/krita/libglobal/kis_debug.cpp b/krita/libglobal/kis_debug.cpp
new file mode 100644
index 00000000000..708b0edc413
--- /dev/null
+++ b/krita/libglobal/kis_debug.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "kis_debug.h"
+
+#include "config-debug.h"
+
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#ifdef __GNUC__
+#define HAVE_BACKTRACE_DEMANGLE
+#include <cxxabi.h>
+#endif
+#endif
+
+#include <string>
+
+
+#if HAVE_BACKTRACE
+static QString maybeDemangledName(char *name)
+{
+#ifdef HAVE_BACKTRACE_DEMANGLE
+ const int len = strlen(name);
+ QByteArray in = QByteArray::fromRawData(name, len);
+ const int mangledNameStart = in.indexOf("(_");
+ if (mangledNameStart >= 0) {
+ const int mangledNameEnd = in.indexOf('+', mangledNameStart + 2);
+ if (mangledNameEnd >= 0) {
+ int status;
+ // if we forget about this line and the one that undoes its effect we don't change the
+ // internal data of the QByteArray::fromRawData() ;)
+ name[mangledNameEnd] = 0;
+ char *demangled = abi::__cxa_demangle(name + mangledNameStart + 1, 0, 0, &status);
+ name[mangledNameEnd] = '+';
+ if (demangled) {
+ QString ret = QString::fromLatin1(name, mangledNameStart + 1) +
+ QString::fromLatin1(demangled) +
+ QString::fromLatin1(name + mangledNameEnd, len - mangledNameEnd);
+ free(demangled);
+ return ret;
+ }
+ }
+ }
+#endif
+ return QString::fromLatin1(name);
+}
+#endif
+
+QString kisBacktrace()
+{
+ QString s;
+#if HAVE_BACKTRACE
+ void *trace[256];
+ int n = backtrace(trace, 256);
+ if (!n) {
+ return s;
+ }
+ char **strings = backtrace_symbols(trace, n);
+
+ s = QLatin1String("[\n");
+
+ for (int i = 0; i < n; ++i)
+ s += QString::number(i) + QLatin1String(": ") +
+ maybeDemangledName(strings[i]) + QLatin1Char('\n');
+ s += QLatin1String("]\n");
+ if (strings) {
+ free(strings);
+ }
+#endif
+ return s;
+}
+
+Q_LOGGING_CATEGORY(_30009, "krita.lib.resources")
+Q_LOGGING_CATEGORY(_41000, "krita.general")
+Q_LOGGING_CATEGORY(_41001, "krita.core")
+Q_LOGGING_CATEGORY(_41002, "krita.registry")
+Q_LOGGING_CATEGORY(_41003, "krita.tools")
+Q_LOGGING_CATEGORY(_41004, "krita.tiles")
+Q_LOGGING_CATEGORY(_41005, "krita.filters")
+Q_LOGGING_CATEGORY(_41006, "krita.plugins")
+Q_LOGGING_CATEGORY(_41007, "krita.ui")
+Q_LOGGING_CATEGORY(_41008, "krita.file")
+Q_LOGGING_CATEGORY(_41009, "krita.math")
+Q_LOGGING_CATEGORY(_41010, "krita.render")
+Q_LOGGING_CATEGORY(_41011, "krita.scripting")
+Q_LOGGING_CATEGORY(_41012, "krita.tablet")
+
+
+
+const char* __methodName(const char *_prettyFunction)
+{
+ std::string prettyFunction(_prettyFunction);
+
+ size_t colons = prettyFunction.find("::");
+ size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
+ size_t end = prettyFunction.rfind("(") - begin;
+
+ return std::string(prettyFunction.substr(begin,end) + "()").c_str();
+}
diff --git a/krita/libglobal/kis_debug.h b/krita/libglobal/kis_debug.h
new file mode 100644
index 00000000000..dc4f0e2c71a
--- /dev/null
+++ b/krita/libglobal/kis_debug.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KIS_DEBUG_H_
+#define KIS_DEBUG_H_
+
+#include <QDebug>
+#include <QLoggingCategory>
+
+#include "kritaglobal_export.h"
+
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_30009();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41000();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41001();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41002();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41003();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41004();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41005();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41006();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41007();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41008();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41009();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41010();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41011();
+extern const KRITAGLOBAL_EXPORT QLoggingCategory &_41012();
+
+#define dbgResources qCDebug(_30009)
+#define dbgKrita qCDebug(_41000)
+#define dbgImage qCDebug(_41001)
+#define dbgRegistry qCDebug(_41002)
+#define dbgTools qCDebug(_41003)
+#define dbgTiles qCDebug(_41004)
+#define dbgFilters qCDebug(_41005)
+#define dbgPlugins qCDebug(_41006)
+#define dbgUI qCDebug(_41007)
+#define dbgFile qCDebug(_41008)
+#define dbgMath qCDebug(_41009)
+#define dbgRender qCDebug(_41010)
+#define dbgScript qCDebug(_41011)
+#define dbgTablet qCDebug(_41012)
+
+#define infoResources qCInfo(_30009)
+#define infoKrita qCInfo(_41000)
+#define infoImage qCInfo(_41001)
+#define infoRegistry qCInfo(_41002)
+#define infoTools qCInfo(_41003)
+#define infoTiles qCInfo(_41004)
+#define infoFilters qCInfo(_41005)
+#define infoPlugins qCInfo(_41006)
+#define infoUI qCInfo(_41007)
+#define infoFile qCInfo(_41008)
+#define infoMath qCInfo(_41009)
+#define infoRender qCInfo(_41010)
+#define infoScript qCInfo(_41011)
+#define infoTablet qCInfo(_41012)
+
+#define warnResources qCWarning(_30009)
+#define warnKrita qCWarning(_41000)
+#define warnImage qCWarning(_41001)
+#define warnRegistry qCWarning(_41002)
+#define warnTools qCWarning(_41003)
+#define warnTiles qCWarning(_41004)
+#define warnFilters qCWarning(_41005)
+#define warnPlugins qCWarning(_41006)
+#define warnUI qCWarning(_41007)
+#define warnFile qCWarning(_41008)
+#define warnMath qCWarning(_41009)
+#define warnRender qCWarning(_41010)
+#define warnScript qCWarning(_41011)
+#define warnTablet qCWarning(_41012)
+
+#define errResources qCCritical(_30009)
+#define errKrita qCCritical(_41000)
+#define errImage qCCritical(_41001)
+#define errRegistry qCCritical(_41002)
+#define errTools qCCritical(_41003)
+#define errTiles qCCritical(_41004)
+#define errFilters qCCritical(_41005)
+#define errPlugins qCCritical(_41006)
+#define errUI qCCritical(_41007)
+#define errFile qCCritical(_41008)
+#define errMath qCCritical(_41009)
+#define errRender qCCritical(_41010)
+#define errScript qCCritical(_41011)
+#define errTablet qCCritical(_41012)
+
+#define fatalResources qCFatal(_30009)
+#define fatalKrita qCFatal(_41000)
+#define fatalImage qCFatal(_41001)
+#define fatalRegistry qCFatal(_41002)
+#define fatalTools qCFatal(_41003)
+#define fatalTiles qCFatal(_41004)
+#define fatalFilters qCFatal(_41005)
+#define fatalPlugins qCFatal(_41006)
+#define fatalUI qCFatal(_41007)
+#define fatalFile qCFatal(_41008)
+#define fatalMath qCFatal(_41009)
+#define fatalRender qCFatal(_41010)
+#define fatalScript qCFatal(_41011)
+#define fatalTablet qCFatal(_41012)
+
+
+KRITAGLOBAL_EXPORT QString kisBacktrace();
+
+/**
+ * Use this macro to display in the output stream the name of a variable followed by its value.
+ */
+#define ppVar( var ) #var << "=" << var
+
+#ifdef __GNUC__
+#define ENTER_FUNCTION() dbgKrita << "Entering" << __func__
+#define LEAVE_FUNCTION() dbgKrita << "Leaving " << __func__
+#else
+#define ENTER_FUNCTION() dbgKrita << "Entering" << "<unknown>"
+#define LEAVE_FUNCTION() dbgKrita << "Leaving " << "<unknown>"
+#endif
+
+# ifndef QT_NO_DEBUG
+# undef Q_ASSERT
+# define Q_ASSERT(cond) if(!(cond)) { errKrita << kisBacktrace(); qt_assert(#cond,__FILE__,__LINE__); } qt_noop()
+# endif
+
+
+#ifdef __GNUC__
+KRITAGLOBAL_EXPORT const char* __methodName(const char *prettyFunction);
+#define __METHOD_NAME__ __methodName(__PRETTY_FUNCTION__)
+#else
+#define __METHOD_NAME__ "<unknown>:<unknown>"
+#endif
+
+#define PREPEND_METHOD(msg) QString("%1: %2").arg(__METHOD_NAME__).arg(msg)
+
+#include "kis_assert.h"
+
+#endif
diff --git a/krita/libglobal/kritaglobal_export.h b/krita/libglobal/kritaglobal_export.h
new file mode 100644
index 00000000000..ff30e9da82f
--- /dev/null
+++ b/krita/libglobal/kritaglobal_export.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of krita
+ Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
+ Copyright (c) 2008 Thomas Zander <zander@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KRITAGLOBAL_EXPORT_H
+#define KRITAGLOBAL_EXPORT_H
+
+#include <kdemacros.h>
+
+/* We use _WIN32/_WIN64 instead of Q_OS_WIN so that this header can be used from C files too */
+#if defined(_WIN32) || defined(_WIN64)
+
+#ifndef KRITAGLOBAL_EXPORT
+# ifdef KRITAGLOBAL_EXPORTS
+# define KRITAGLOBAL_EXPORT KDE_EXPORT
+# else
+# define KRITAGLOBAL_EXPORT KDE_IMPORT
+# endif
+#endif
+
+#else // not windows
+
+#define KRITAGLOBAL_EXPORT KDE_EXPORT
+
+#endif /* not windows */
+
+/* Now the same for Krita*_TEST_EXPORT, if compiling with unit tests enabled */
+#ifdef COMPILING_TESTS
+# if defined _WIN32 || defined _WIN64
+# if defined(KRITAGLOBAL_EXPORTS)
+# define KRITAGLOBAL_TEST_EXPORT KDE_EXPORT
+# else
+# define KRITAGLOBAL_TEST_EXPORT KDE_IMPORT
+# endif
+# else /* not windows */
+# define KRITAGLOBAL_TEST_EXPORT KDE_EXPORT
+# endif
+#else /* not compiling tests */
+# define KRITAGLOBAL_TEST_EXPORT
+#endif
+
+#endif /* KRITAGLOBAL_EXPORT_H */
diff --git a/krita/libpsd/CMakeLists.txt b/krita/libpsd/CMakeLists.txt
index c2b17290e8a..2be7e5a2205 100644
--- a/krita/libpsd/CMakeLists.txt
+++ b/krita/libpsd/CMakeLists.txt
@@ -1,40 +1,40 @@
include_directories(${KOTEXT_INCLUDES})
include_directories(${CMAKE_BINARY_DIR}/krita/libpsd) #For kispsd_include.h
add_subdirectory( tests )
set(kritapsd_LIB_SRCS
psd_utils.cpp
psd.cpp
compression.cpp
psd_pattern.cpp
asl/kis_asl_reader.cpp
asl/kis_asl_reader_utils.cpp
asl/kis_asl_xml_parser.cpp
asl/kis_asl_object_catcher.cpp
asl/kis_asl_callback_object_catcher.cpp
asl/kis_asl_xml_writer.cpp
asl/kis_asl_writer_utils.cpp
asl/kis_asl_patterns_writer.cpp
asl/kis_asl_writer.cpp
)
add_library(kritapsd SHARED ${kritapsd_LIB_SRCS} )
generate_export_header(kritapsd BASE_NAME kritapsd)
if (WIN32)
target_link_libraries(kritapsd pigmentcms kritaglobal KF5::KDELibs4Support ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${WIN32_PLATFORM_NET_LIBS})
else (WIN32)
target_link_libraries(kritapsd pigmentcms kritaglobal KF5::KDELibs4Support ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
endif (WIN32)
-target_link_libraries(kritapsd LINK_INTERFACE_LIBRARIES ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
+target_link_libraries(kritapsd LINK_INTERFACE_LIBRARIES kritaglobal ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
set_target_properties(kritapsd PROPERTIES
VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
install(TARGETS kritapsd ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/krita/libpsd/asl/kis_asl_callback_object_catcher.cpp b/krita/libpsd/asl/kis_asl_callback_object_catcher.cpp
index f0f360e12c3..235239aa2d2 100644
--- a/krita/libpsd/asl/kis_asl_callback_object_catcher.cpp
+++ b/krita/libpsd/asl/kis_asl_callback_object_catcher.cpp
@@ -1,258 +1,258 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_callback_object_catcher.h"
#include <QHash>
#include <QString>
#include <QPointF>
#include <QColor>
#include "kis_debug.h"
typedef QHash<QString, ASLCallbackDouble> MapHashDouble;
typedef QHash<QString, ASLCallbackInteger> MapHashInt;
struct EnumMapping {
EnumMapping(const QString &_typeId, ASLCallbackString _map)
: typeId(_typeId),
map(_map)
{
}
QString typeId;
ASLCallbackString map;
};
typedef QHash<QString, EnumMapping> MapHashEnum;
struct UnitFloatMapping {
UnitFloatMapping(const QString &_unit, ASLCallbackDouble _map)
: unit(_unit),
map(_map)
{
}
QString unit;
ASLCallbackDouble map;
};
typedef QHash<QString, UnitFloatMapping> MapHashUnitFloat;
typedef QHash<QString, ASLCallbackString> MapHashText;
typedef QHash<QString, ASLCallbackBoolean> MapHashBoolean;
typedef QHash<QString, ASLCallbackColor> MapHashColor;
typedef QHash<QString, ASLCallbackPoint> MapHashPoint;
typedef QHash<QString, ASLCallbackCurve> MapHashCurve;
typedef QHash<QString, ASLCallbackPattern> MapHashPattern;
typedef QHash<QString, ASLCallbackPatternRef> MapHashPatternRef;
typedef QHash<QString, ASLCallbackGradient> MapHashGradient;
struct KisAslCallbackObjectCatcher::Private
{
MapHashDouble mapDouble;
MapHashInt mapInteger;
MapHashEnum mapEnum;
MapHashUnitFloat mapUnitFloat;
MapHashText mapText;
MapHashBoolean mapBoolean;
MapHashColor mapColor;
MapHashPoint mapPoint;
MapHashCurve mapCurve;
MapHashPattern mapPattern;
MapHashPatternRef mapPatternRef;
MapHashGradient mapGradient;
ASLCallbackNewStyle newStyleCallback;
};
KisAslCallbackObjectCatcher::KisAslCallbackObjectCatcher()
: m_d(new Private)
{
}
KisAslCallbackObjectCatcher::~KisAslCallbackObjectCatcher()
{
}
template <class HashType, typename T>
inline void passToCallback(const QString &path, const HashType &hash, const T &value)
{
typename HashType::const_iterator it = hash.find(path);
if (it != hash.constEnd()) {
(*it)(value);
}
}
void KisAslCallbackObjectCatcher::addDouble(const QString &path, double value)
{
passToCallback(path, m_d->mapDouble, value);
}
void KisAslCallbackObjectCatcher::addInteger(const QString &path, int value)
{
passToCallback(path, m_d->mapInteger, value);
}
void KisAslCallbackObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value)
{
MapHashEnum::const_iterator it = m_d->mapEnum.find(path);
if (it != m_d->mapEnum.constEnd()) {
if (it->typeId == typeId) {
it->map(value);
} else {
- qWarning() << "KisAslCallbackObjectCatcher::addEnum: inconsistent typeId" << ppVar(typeId) << ppVar(it->typeId);
+ warnKrita << "KisAslCallbackObjectCatcher::addEnum: inconsistent typeId" << ppVar(typeId) << ppVar(it->typeId);
}
}
}
void KisAslCallbackObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value)
{
MapHashUnitFloat::const_iterator it = m_d->mapUnitFloat.find(path);
if (it != m_d->mapUnitFloat.constEnd()) {
if (it->unit == unit) {
it->map(value);
} else {
- qWarning() << "KisAslCallbackObjectCatcher::addUnitFloat: inconsistent unit" << ppVar(unit) << ppVar(it->unit);
+ warnKrita << "KisAslCallbackObjectCatcher::addUnitFloat: inconsistent unit" << ppVar(unit) << ppVar(it->unit);
}
}
}
void KisAslCallbackObjectCatcher::addText(const QString &path, const QString &value)
{
passToCallback(path, m_d->mapText, value);
}
void KisAslCallbackObjectCatcher::addBoolean(const QString &path, bool value)
{
passToCallback(path, m_d->mapBoolean, value);
}
void KisAslCallbackObjectCatcher::addColor(const QString &path, const QColor &value)
{
passToCallback(path, m_d->mapColor, value);
}
void KisAslCallbackObjectCatcher::addPoint(const QString &path, const QPointF &value)
{
passToCallback(path, m_d->mapPoint, value);
}
void KisAslCallbackObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points)
{
MapHashCurve::const_iterator it = m_d->mapCurve.find(path);
if (it != m_d->mapCurve.constEnd()) {
(*it)(name, points);
}
}
void KisAslCallbackObjectCatcher::addPattern(const QString &path, const KoPattern *value)
{
passToCallback(path, m_d->mapPattern, value);
}
void KisAslCallbackObjectCatcher::addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName)
{
MapHashPatternRef::const_iterator it = m_d->mapPatternRef.find(path);
if (it != m_d->mapPatternRef.constEnd()) {
(*it)(patternUuid, patternName);
}
}
void KisAslCallbackObjectCatcher::addGradient(const QString &path, KoAbstractGradientSP value)
{
passToCallback(path, m_d->mapGradient, value);
}
void KisAslCallbackObjectCatcher::newStyleStarted()
{
if (m_d->newStyleCallback) {
m_d->newStyleCallback();
}
}
/*****************************************************************/
/* Subscription methods */
/*****************************************************************/
void KisAslCallbackObjectCatcher::subscribeDouble(const QString &path, ASLCallbackDouble callback)
{
m_d->mapDouble.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeInteger(const QString &path, ASLCallbackInteger callback)
{
m_d->mapInteger.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback)
{
m_d->mapEnum.insert(path, EnumMapping(typeId, callback));
}
void KisAslCallbackObjectCatcher::subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback)
{
m_d->mapUnitFloat.insert(path, UnitFloatMapping(unit, callback));
}
void KisAslCallbackObjectCatcher::subscribeText(const QString &path, ASLCallbackString callback)
{
m_d->mapText.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeBoolean(const QString &path, ASLCallbackBoolean callback)
{
m_d->mapBoolean.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeColor(const QString &path, ASLCallbackColor callback)
{
m_d->mapColor.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePoint(const QString &path, ASLCallbackPoint callback)
{
m_d->mapPoint.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeCurve(const QString &path, ASLCallbackCurve callback)
{
m_d->mapCurve.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePattern(const QString &path, ASLCallbackPattern callback)
{
m_d->mapPattern.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePatternRef(const QString &path, ASLCallbackPatternRef callback)
{
m_d->mapPatternRef.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeGradient(const QString &path, ASLCallbackGradient callback)
{
m_d->mapGradient.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeNewStyleStarted(ASLCallbackNewStyle callback)
{
m_d->newStyleCallback = callback;
}
diff --git a/krita/libpsd/asl/kis_asl_object_catcher.cpp b/krita/libpsd/asl/kis_asl_object_catcher.cpp
index f6d9c7dd981..35282d3fcdd 100644
--- a/krita/libpsd/asl/kis_asl_object_catcher.cpp
+++ b/krita/libpsd/asl/kis_asl_object_catcher.cpp
@@ -1,96 +1,96 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_object_catcher.h"
#include <QString>
#include <QPointF>
#include <QColor>
#include "kis_debug.h"
-
+#include "kis_types.h"
KisAslObjectCatcher::KisAslObjectCatcher()
: m_arrayMode(false)
{
}
KisAslObjectCatcher::~KisAslObjectCatcher()
{
}
void KisAslObjectCatcher::addDouble(const QString &path, double value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "double" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "double" << value;
}
void KisAslObjectCatcher::addInteger(const QString &path, int value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "int" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "int" << value;
}
void KisAslObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "enum" << ppVar(typeId) << ppVar(value);
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "enum" << ppVar(typeId) << ppVar(value);
}
void KisAslObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "unitfloat" << ppVar(unit) << ppVar(value);
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "unitfloat" << ppVar(unit) << ppVar(value);
}
void KisAslObjectCatcher::addText(const QString &path, const QString &value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "text" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "text" << value;
}
void KisAslObjectCatcher::addBoolean(const QString &path, bool value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "bool" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "bool" << value;
}
void KisAslObjectCatcher::addColor(const QString &path, const QColor &value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "color" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "color" << value;
}
void KisAslObjectCatcher::addPoint(const QString &path, const QPointF &value) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "point" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "point" << value;
}
void KisAslObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points) {
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "curve" << name << ppVar(points.size());
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "curve" << name << ppVar(points.size());
}
void KisAslObjectCatcher::addPattern(const QString &path, const KoPattern *value)
{
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern" << value;
}
void KisAslObjectCatcher::addPatternRef(const QString &path, const QString &patternUuid, const QString &patternName)
{
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern-ref" << ppVar(patternUuid) << ppVar(patternName);
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern-ref" << ppVar(patternUuid) << ppVar(patternName);
}
void KisAslObjectCatcher::addGradient(const QString &path, KoAbstractGradientSP value)
{
- qDebug() << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "gradient" << value;
+ dbgKrita << "Unhandled:" << (m_arrayMode ? "[A]" : "[ ]") << path << "gradient" << value;
}
void KisAslObjectCatcher::newStyleStarted()
{
- qDebug() << "Unhandled:" << "new style started";
+ dbgKrita << "Unhandled:" << "new style started";
}
void KisAslObjectCatcher::setArrayMode(bool value) {
m_arrayMode = value;
}
diff --git a/krita/libpsd/asl/kis_asl_reader.cpp b/krita/libpsd/asl/kis_asl_reader.cpp
index 951dae55630..6325780f18f 100644
--- a/krita/libpsd/asl/kis_asl_reader.cpp
+++ b/krita/libpsd/asl/kis_asl_reader.cpp
@@ -1,658 +1,658 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_reader.h"
#include "kis_dom_utils.h"
#include <stdexcept>
#include <string>
#include <QDomDocument>
#include <QIODevice>
#include <QBuffer>
#include "psd_utils.h"
#include "psd.h"
#include "compression.h"
#include "kis_offset_on_exit_verifier.h"
#include "kis_asl_writer_utils.h"
#include "kis_asl_reader_utils.h"
namespace Private {
/**
* Numerical fetch functions
*
* We read numbers and convert them to strings to be able to store
* them in XML.
*/
QString readDoubleAsString(QIODevice *device) {
double value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::Private::numberToString(value);
}
QString readIntAsString(QIODevice *device) {
quint32 value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::Private::numberToString(value);
}
QString readBoolAsString(QIODevice *device) {
quint8 value = 0.0;
SAFE_READ_EX(device, value);
return KisDomUtils::Private::numberToString(value);
}
/**
* XML generation functions
*
* Add a node and fill the corresponding attributes
*/
QDomElement appendXMLNodeCommon(const QString &key, const QString &value, const QString &type, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = doc->createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", type);
el.setAttribute("value", value);
parent->appendChild(el);
return el;
}
QDomElement appendXMLNodeCommonNoValue(const QString &key, const QString &type, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = doc->createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", type);
parent->appendChild(el);
return el;
}
void appendIntegerXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Integer", parent, doc);
}
void appendDoubleXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Double", parent, doc);
}
void appendTextXMLNode(const QString &key, const QString &value, QDomElement *parent, QDomDocument *doc)
{
appendXMLNodeCommon(key, value, "Text", parent, doc);
}
void appendPointXMLNode(const QString &key, const QPointF &pt, QDomElement *parent, QDomDocument *doc)
{
QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
el.setAttribute("classId", "CrPt");
el.setAttribute("name", "");
appendDoubleXMLNode("Hrzn", KisDomUtils::Private::numberToString(pt.x()), &el, doc);
appendDoubleXMLNode("Vrtc", KisDomUtils::Private::numberToString(pt.x()), &el, doc);
}
/**
* ASL -> XML parsing functions
*/
void readDescriptor(QIODevice *device,
const QString &key,
QDomElement *parent,
QDomDocument *doc);
void readChildObject(QIODevice *device,
QDomElement *parent,
QDomDocument *doc,
bool skipKey = false)
{
using namespace KisAslReaderUtils;
QString key;
if (!skipKey) {
key = readVarString(device);
}
QString OSType = readFixedString(device);
- //qDebug() << "Child" << ppVar(key) << ppVar(OSType);
+ //dbgKrita << "Child" << ppVar(key) << ppVar(OSType);
if (OSType == "obj ") {
qFatal("no implemented");
} else if (OSType == "Objc" || OSType == "GlbO") {
readDescriptor(device, key, parent, doc);
} else if (OSType == "VlLs") {
quint32 numItems = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numItems);
QDomElement el = appendXMLNodeCommonNoValue(key, "List", parent, doc);
for (quint32 i = 0; i < numItems; i++) {
readChildObject(device, &el, doc, true);
}
} else if (OSType == "doub") {
appendDoubleXMLNode(key, readDoubleAsString(device), parent, doc);
} else if (OSType == "UntF") {
const QString unit = readFixedString(device);
const QString value = readDoubleAsString(device);
QDomElement el = appendXMLNodeCommon(key, value, "UnitFloat", parent, doc);
el.setAttribute("unit", unit);
} else if (OSType == "TEXT") {
QString unicodeString = readUnicodeString(device);
appendTextXMLNode(key, unicodeString, parent, doc);
} else if (OSType == "enum") {
const QString typeId = readVarString(device);
const QString value = readVarString(device);
QDomElement el = appendXMLNodeCommon(key, value, "Enum", parent, doc);
el.setAttribute("typeId", typeId);
} else if (OSType == "long") {
appendIntegerXMLNode(key, readIntAsString(device), parent, doc);
} else if (OSType == "bool") {
const QString value = readBoolAsString(device);
appendXMLNodeCommon(key, value, "Boolean", parent, doc);
} else if (OSType == "type") {
qFatal("no implemented");
} else if (OSType == "GlbC") {
qFatal("no implemented");
} else if (OSType == "alis") {
qFatal("no implemented");
} else if (OSType == "tdta") {
qFatal("no implemented");
}
}
void readDescriptor(QIODevice *device,
const QString &key,
QDomElement *parent,
QDomDocument *doc)
{
using namespace KisAslReaderUtils;
QString name = readUnicodeString(device);
QString classId = readVarString(device);
quint32 numChildren = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numChildren);
QDomElement el = appendXMLNodeCommonNoValue(key, "Descriptor", parent, doc);
el.setAttribute("classId", classId);
el.setAttribute("name", name);
- //qDebug() << "Descriptor" << ppVar(key) << ppVar(classId) << ppVar(numChildren);
+ //dbgKrita << "Descriptor" << ppVar(key) << ppVar(classId) << ppVar(numChildren);
for (quint32 i = 0; i < numChildren; i++) {
readChildObject(device, &el, doc);
}
}
QImage readVirtualArrayList(QIODevice *device,
int numPlanes)
{
using namespace KisAslReaderUtils;
quint32 arrayVersion = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, arrayVersion);
if (arrayVersion != 3) {
throw ASLParseException("VAList version is not '3'!");
}
quint32 arrayLength = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, arrayLength);
SETUP_OFFSET_VERIFIER(vaEndVerifier, device, arrayLength, 100);
quint32 x0, y0, x1, y1;
SAFE_READ_EX(device, y0);
SAFE_READ_EX(device, x0);
SAFE_READ_EX(device, y1);
SAFE_READ_EX(device, x1);
QRect arrayRect(x0, y0, x1 - x0, y1 - y0);
quint32 numberOfChannels = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numberOfChannels);
if (numberOfChannels != 24) {
throw ASLParseException("VAList: Krita doesn't support ASL files with 'numberOfChannels' flag not equal to 24 (it is not documented)!");
}
- // qDebug() << ppVar(arrayVersion);
- // qDebug() << ppVar(arrayLength);
- // qDebug() << ppVar(arrayRect);
- // qDebug() << ppVar(numberOfChannels);
+ // dbgKrita << ppVar(arrayVersion);
+ // dbgKrita << ppVar(arrayLength);
+ // dbgKrita << ppVar(arrayRect);
+ // dbgKrita << ppVar(numberOfChannels);
if (numPlanes != 1 && numPlanes != 3) {
throw ASLParseException("VAList: unsupported number of planes!");
}
QVector<QByteArray> dataPlanes;
dataPlanes.resize(3);
for (int i = 0; i < numPlanes; i++) {
quint32 arrayWritten = GARBAGE_VALUE_MARK;
if (!psdread(device, &arrayWritten) || !arrayWritten) {
throw ASLParseException("VAList plane has not-written flag set!");
}
quint32 arrayPlaneLength = GARBAGE_VALUE_MARK;
if (!psdread(device, &arrayPlaneLength) || !arrayPlaneLength) {
throw ASLParseException("VAList has plane length set to zero!");
}
SETUP_OFFSET_VERIFIER(planeEndVerifier, device, arrayPlaneLength, 0);
qint64 nextPos = device->pos() + arrayPlaneLength;
quint32 pixelDepth1 = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, pixelDepth1);
quint32 x0, y0, x1, y1;
SAFE_READ_EX(device, y0);
SAFE_READ_EX(device, x0);
SAFE_READ_EX(device, y1);
SAFE_READ_EX(device, x1);
QRect planeRect(x0, y0, x1 - x0, y1 - y0);
if (planeRect != arrayRect) {
throw ASLParseException("VAList: planes are not uniform. Not supported yet!");
}
quint16 pixelDepth2 = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, pixelDepth2);
quint8 useCompression = 9;
SAFE_READ_EX(device, useCompression);
- // qDebug() << "plane index:" << ppVar(i);
- // qDebug() << ppVar(arrayWritten);
- // qDebug() << ppVar(arrayPlaneLength);
- // qDebug() << ppVar(pixelDepth1);
- // qDebug() << ppVar(planeRect);
- // qDebug() << ppVar(pixelDepth2);
- // qDebug() << ppVar(useCompression);
+ // dbgKrita << "plane index:" << ppVar(i);
+ // dbgKrita << ppVar(arrayWritten);
+ // dbgKrita << ppVar(arrayPlaneLength);
+ // dbgKrita << ppVar(pixelDepth1);
+ // dbgKrita << ppVar(planeRect);
+ // dbgKrita << ppVar(pixelDepth2);
+ // dbgKrita << ppVar(useCompression);
if (pixelDepth1 != pixelDepth2) {
throw ASLParseException("VAList: two pixel depths of the plane are not equal (it is not documented)!");
}
if (pixelDepth1 != 8) {
throw ASLParseException("VAList: supported pixel depth of the plane in 8 only!");
}
const int dataLength = planeRect.width() * planeRect.height();
if (useCompression == Compression::Uncompressed) {
dataPlanes[i] = device->read(dataLength);
} else if (useCompression == Compression::RLE) {
const int numRows = planeRect.height();
QVector<quint16> rowSizes;
rowSizes.resize(numRows);
for (int row = 0; row < numRows; row++) {
quint16 rowSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, rowSize);
rowSizes[row] = rowSize;
}
for (int row = 0; row < numRows; row++) {
const quint16 rowSize = rowSizes[row];
QByteArray compressedData = device->read(rowSize);
if (compressedData.size() != rowSize) {
throw ASLParseException("VAList: failed to read compressed data!");
}
QByteArray uncompressedData = Compression::uncompress(planeRect.width(), compressedData, Compression::RLE);
if (uncompressedData.size() != planeRect.width()) {
throw ASLParseException("VAList: failed to decompress data!");
}
dataPlanes[i].append(uncompressedData);
}
} else {
throw ASLParseException("VAList: ZIP compression is not implemented yet!");
}
if (dataPlanes[i].size() != dataLength) {
throw ASLParseException("VAList: failed to read/uncompress data plane!");
}
device->seek(nextPos);
}
QImage image(arrayRect.size(), QImage::Format_ARGB32);
const int dataLength = arrayRect.width() * arrayRect.height();
quint8 *dstPtr = image.bits();
for (int i = 0; i < dataLength; i++) {
for (int j = 2; j >= 0; j--) {
int plane = qMin(numPlanes, j);
*dstPtr++ = dataPlanes[plane][i];
}
*dstPtr++ = 0xFF;
}
#if 0
static int i = -1; i++;
QString filename = QString("pattern_image_%1.png").arg(i);
- qDebug() << "### dumping pattern image" << ppVar(filename);
+ dbgKrita << "### dumping pattern image" << ppVar(filename);
image.save(filename);
#endif
return image;
}
qint64 readPattern(QIODevice *device,
QDomElement *parent,
QDomDocument *doc)
{
using namespace KisAslReaderUtils;
quint32 patternSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternSize);
// patterns are always aligned by 4 bytes
patternSize = KisAslWriterUtils::alignOffsetCeil(patternSize, 4);
SETUP_OFFSET_VERIFIER(patternEndVerifier, device, patternSize, 0);
quint32 patternVersion = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternVersion);
if (patternVersion != 1) {
throw ASLParseException("Pattern version is not \'1\'");
}
quint32 patternImageMode = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternImageMode);
quint16 patternHeight = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternHeight);
quint16 patternWidth = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternWidth);
QString patternName;
psdread_unicodestring(device, patternName);
QString patternUuid = readPascalString(device);
- // qDebug() << "--";
- // qDebug() << ppVar(patternSize);
- // qDebug() << ppVar(patternImageMode);
- // qDebug() << ppVar(patternHeight);
- // qDebug() << ppVar(patternWidth);
- // qDebug() << ppVar(patternName);
- // qDebug() << ppVar(patternUuid);
+ // dbgKrita << "--";
+ // dbgKrita << ppVar(patternSize);
+ // dbgKrita << ppVar(patternImageMode);
+ // dbgKrita << ppVar(patternHeight);
+ // dbgKrita << ppVar(patternWidth);
+ // dbgKrita << ppVar(patternName);
+ // dbgKrita << ppVar(patternUuid);
int numPlanes = 0;
psd_color_mode mode = static_cast<psd_color_mode>(patternImageMode);
switch (mode) {
case MultiChannel:
case Grayscale:
numPlanes = 1;
break;
case RGB:
numPlanes = 3;
break;
default: {
QString msg = QString("Unsupported image mode: %1!").arg(mode);
throw ASLParseException(msg);
}
}
/**
* Create XML data
*/
QDomElement pat = doc->createElement("node");
pat.setAttribute("classId", "KisPattern");
pat.setAttribute("type", "Descriptor");
pat.setAttribute("name", "");
QBuffer patternBuf;
patternBuf.open(QIODevice::WriteOnly);
{ // ensure we don't keep resources for too long
QString fileName = QString("%1.pat").arg(patternUuid);
QImage patternImage = readVirtualArrayList(device, numPlanes);
KoPattern realPattern(patternImage, patternName, fileName);
realPattern.savePatToDevice(&patternBuf);
}
/**
* We are loading the pattern and convert it into ARGB right away,
* so we need not store real image mode and size of the pattern
* externally.
*/
appendTextXMLNode("Nm ", patternName, &pat, doc);
appendTextXMLNode("Idnt", patternUuid, &pat, doc);
QDomCDATASection dataSection = doc->createCDATASection(qCompress(patternBuf.buffer()).toBase64());
QDomElement dataElement = doc->createElement("node");
dataElement.setAttribute("type", "KisPatternData");
dataElement.setAttribute("key", "Data");
dataElement.appendChild(dataSection);
pat.appendChild(dataElement);
parent->appendChild(pat);
return sizeof(patternSize) + patternSize;
}
QDomDocument readFileImpl(QIODevice *device)
{
using namespace KisAslReaderUtils;
QDomDocument doc;
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
{
quint16 stylesVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesVersion, 2);
}
{
quint32 aslSignature = GARBAGE_VALUE_MARK;
const quint32 refSignature = 0x3842534c; // '8BSL' in little-endian
SAFE_READ_SIGNATURE_EX(device, aslSignature, refSignature);
}
{
quint16 patternsVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, patternsVersion, 3);
}
// Patterns
{
quint32 patternsSize = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, patternsSize);
if (patternsSize > 0) {
SETUP_OFFSET_VERIFIER(patternsSectionVerifier, device, patternsSize, 0);
QDomElement patternsRoot = doc.createElement("node");
patternsRoot.setAttribute("type", "List");
patternsRoot.setAttribute("key", "Patterns");
root.appendChild(patternsRoot);
try {
qint64 bytesRead = 0;
while (bytesRead < patternsSize) {
qint64 chunk = readPattern(device, &patternsRoot, &doc);
bytesRead += chunk;
}
} catch (ASLParseException &e) {
- qWarning() << "WARNING: ASL (emb. pattern):" << e.what();
+ warnKrita << "WARNING: ASL (emb. pattern):" << e.what();
}
}
}
// Styles
quint32 numStyles = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, numStyles);
for (int i = 0; i < (int)numStyles; i++) {
quint32 bytesToRead = GARBAGE_VALUE_MARK;
SAFE_READ_EX(device, bytesToRead);
SETUP_OFFSET_VERIFIER(singleStyleSectionVerifier, device, bytesToRead, 0);
{
quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesFormatVersion, 16);
}
readDescriptor(device, "", &root, &doc);
{
quint32 stylesFormatVersion = GARBAGE_VALUE_MARK;
SAFE_READ_SIGNATURE_EX(device, stylesFormatVersion, 16);
}
readDescriptor(device, "", &root, &doc);
}
return doc;
}
} // namespace
QDomDocument KisAslReader::readFile(QIODevice *device)
{
QDomDocument doc;
if (device->isSequential()) {
- qWarning() << "WARNING: *** KisAslReader::readFile: the supplied"
+ warnKrita << "WARNING: *** KisAslReader::readFile: the supplied"
<< "IO device is sequential. Chances are that"
<< "the layer style will *not* be loaded correctly!";
}
try {
doc = Private::readFileImpl(device);
} catch (KisAslReaderUtils::ASLParseException &e) {
- qWarning() << "WARNING: ASL:" << e.what();
+ warnKrita << "WARNING: ASL:" << e.what();
}
return doc;
}
QDomDocument KisAslReader::readLfx2PsdSection(QIODevice *device)
{
QDomDocument doc;
if (device->isSequential()) {
- qWarning() << "WARNING: *** KisAslReader::readLfx2PsdSection: the supplied"
+ warnKrita << "WARNING: *** KisAslReader::readLfx2PsdSection: the supplied"
<< "IO device is sequential. Chances are that"
<< "the layer style will *not* be loaded correctly!";
}
try {
{
quint32 objectEffectsVersion = GARBAGE_VALUE_MARK;
const quint32 ref = 0x00;
SAFE_READ_SIGNATURE_EX(device, objectEffectsVersion, ref);
}
{
quint32 descriptorVersion = GARBAGE_VALUE_MARK;
const quint32 ref = 0x10;
SAFE_READ_SIGNATURE_EX(device, descriptorVersion, ref);
}
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
Private::readDescriptor(device, "", &root, &doc);
} catch (KisAslReaderUtils::ASLParseException &e) {
- qWarning() << "WARNING: PSD: lfx2 section:" << e.what();
+ warnKrita << "WARNING: PSD: lfx2 section:" << e.what();
}
return doc;
}
QDomDocument KisAslReader::readPsdSectionPattern(QIODevice *device, qint64 bytesLeft)
{
QDomDocument doc;
QDomElement root = doc.createElement("asl");
doc.appendChild(root);
QDomElement pat = doc.createElement("node");
root.appendChild(pat);
pat.setAttribute("classId", "Patterns");
pat.setAttribute("type", "Descriptor");
pat.setAttribute("name", "");
try {
qint64 bytesRead = 0;
while (bytesRead < bytesLeft) {
qint64 chunk = Private::readPattern(device, &pat, &doc);
bytesRead += chunk;
}
} catch (KisAslReaderUtils::ASLParseException &e) {
- qWarning() << "WARNING: PSD (emb. pattern):" << e.what();
+ warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
}
return doc;
}
diff --git a/krita/libpsd/asl/kis_asl_writer.cpp b/krita/libpsd/asl/kis_asl_writer.cpp
index 23659c0d886..1af324254f0 100644
--- a/krita/libpsd/asl/kis_asl_writer.cpp
+++ b/krita/libpsd/asl/kis_asl_writer.cpp
@@ -1,278 +1,278 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_writer.h"
#include <QDomDocument>
#include <QIODevice>
#include "kis_dom_utils.h"
#include "kis_debug.h"
#include "psd_utils.h"
#include "kis_asl_patterns_writer.h"
#include "kis_asl_writer_utils.h"
namespace Private {
using namespace KisAslWriterUtils;
void parseElement(const QDomElement &el, QIODevice *device, bool forceTypeInfo = false)
{
KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
QString type = el.attribute("type", "<unknown>");
QString key = el.attribute("key", "");
// should be filtered on a heigher level
KIS_ASSERT_RECOVER_RETURN(key != "Patterns");
if (type == "Descriptor") {
if (!key.isEmpty()) {
writeVarString(key, device);
}
if (!key.isEmpty() || forceTypeInfo) {
writeFixedString("Objc", device);
}
QString classId = el.attribute("classId", "");
QString name = el.attribute("name", "");
writeUnicodeString(name, device);
writeVarString(classId, device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device);
child = child.nextSibling();
}
} else if (type == "List") {
writeVarString(key, device);
writeFixedString("VlLs", device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device, true);
child = child.nextSibling();
}
} else if (type == "Double") {
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("doub", device);
SAFE_WRITE_EX(device, v);
} else if (type == "UnitFloat") {
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
QString unit = el.attribute("unit", "#Pxl");
writeVarString(key, device);
writeFixedString("UntF", device);
writeFixedString(unit, device);
SAFE_WRITE_EX(device, v);
} else if (type == "Text") {
QString v = el.attribute("value", "");
writeVarString(key, device);
writeFixedString("TEXT", device);
writeUnicodeString(v, device);
} else if (type == "Enum") {
QString v = el.attribute("value", "");
QString typeId = el.attribute("typeId", "DEAD");
writeVarString(key, device);
writeFixedString("enum", device);
writeVarString(typeId, device);
writeVarString(v, device);
} else if (type == "Integer") {
quint32 v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("long", device);
SAFE_WRITE_EX(device, v);
} else if (type == "Boolean") {
quint8 v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("bool", device);
SAFE_WRITE_EX(device, v);
} else {
- qWarning() << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key);
+ warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key);
}
}
int calculateNumStyles(const QDomElement &root)
{
int numStyles = 0;
QDomNode child = root.firstChild();
while (!child.isNull()) {
QDomElement el = child.toElement();
QString classId = el.attribute("classId", "");
if (classId == "null") {
numStyles++;
}
child = child.nextSibling();
}
return numStyles;
}
void writeFileImpl(QIODevice *device, const QDomDocument &doc)
{
{
quint16 stylesVersion = 2;
SAFE_WRITE_EX(device, stylesVersion);
}
{
QString signature("8BSL");
if (!device->write(signature.toAscii().data(), 4)) {
throw ASLWriteException("Failed to write ASL signature");
}
}
{
quint16 patternsVersion = 3;
SAFE_WRITE_EX(device, patternsVersion);
}
{
KisAslWriterUtils::OffsetStreamPusher<quint32> patternsSizeField(device);
KisAslPatternsWriter patternsWriter(doc, device);
patternsWriter.writePatterns();
}
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");
int numStyles = calculateNumStyles(root);
KIS_ASSERT_RECOVER_RETURN(numStyles > 0);
{
quint32 numStylesTag = numStyles;
SAFE_WRITE_EX(device, numStylesTag);
}
QDomNode child = root.firstChild();
for (int styleIndex = 0; styleIndex < numStyles; styleIndex++) {
KisAslWriterUtils::OffsetStreamPusher<quint32> theOnlyStyleSizeField(device);
KIS_ASSERT_RECOVER_RETURN(!child.isNull());
{
quint32 stylesFormatVersion = 16;
SAFE_WRITE_EX(device, stylesFormatVersion);
}
while (!child.isNull()) {
QDomElement el = child.toElement();
QString key = el.attribute("key", "");
if (key != "Patterns") break;
child = child.nextSibling();
}
parseElement(child.toElement(), device);
child = child.nextSibling();
{
quint32 stylesFormatVersion = 16;
SAFE_WRITE_EX(device, stylesFormatVersion);
}
parseElement(child.toElement(), device);
child = child.nextSibling();
// ASL files' size should be 4-bytes aligned
const qint64 paddingSize = 4 - (device->pos() & 0x3);
if (paddingSize != 4) {
QByteArray padding(paddingSize, '\0');
device->write(padding);
}
}
}
void writePsdLfx2SectionImpl(QIODevice *device, const QDomDocument &doc)
{
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");
int numStyles = calculateNumStyles(root);
KIS_ASSERT_RECOVER_RETURN(numStyles == 1);
{
quint32 objectEffectsVersion = 0;
SAFE_WRITE_EX(device, objectEffectsVersion);
}
{
quint32 descriptorVersion = 16;
SAFE_WRITE_EX(device, descriptorVersion);
}
QDomNode child = root.firstChild();
while (!child.isNull()) {
QDomElement el = child.toElement();
QString key = el.attribute("key", "");
if (key != "Patterns") break;
child = child.nextSibling();
}
parseElement(child.toElement(), device);
child = child.nextSibling();
// ASL files' size should be 4-bytes aligned
const qint64 paddingSize = 4 - (device->pos() & 0x3);
if (paddingSize != 4) {
QByteArray padding(paddingSize, '\0');
device->write(padding);
}
}
} // namespace
void KisAslWriter::writeFile(QIODevice *device, const QDomDocument &doc)
{
try {
Private::writeFileImpl(device, doc);
} catch (Private::ASLWriteException &e) {
- qWarning() << "WARNING: ASL:" << e.what();
+ warnKrita << "WARNING: ASL:" << e.what();
}
}
void KisAslWriter::writePsdLfx2SectionEx(QIODevice *device, const QDomDocument &doc)
{
Private::writePsdLfx2SectionImpl(device, doc);
}
diff --git a/krita/libpsd/asl/kis_asl_writer_utils.cpp b/krita/libpsd/asl/kis_asl_writer_utils.cpp
index 36ea82eb5fa..1ee0bd9f81f 100644
--- a/krita/libpsd/asl/kis_asl_writer_utils.cpp
+++ b/krita/libpsd/asl/kis_asl_writer_utils.cpp
@@ -1,112 +1,112 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_writer_utils.h"
#include <QUuid>
#include "KoPattern.h"
#include "kis_debug.h"
namespace KisAslWriterUtils {
void writeRect(const QRect &rect, QIODevice *device)
{
{
const quint32 rectY0 = rect.y();
SAFE_WRITE_EX(device, rectY0);
}
{
const quint32 rectX0 = rect.x();
SAFE_WRITE_EX(device, rectX0);
}
{
const quint32 rectY1 = rect.y() + rect.height();
SAFE_WRITE_EX(device, rectY1);
}
{
const quint32 rectX1 = rect.x() + rect.width();
SAFE_WRITE_EX(device, rectX1);
}
}
void writeUnicodeString(const QString &value, QIODevice *device)
{
quint32 len = value.length() + 1;
SAFE_WRITE_EX(device, len);
const quint16 *ptr = value.utf16();
for (quint32 i = 0; i < len; i++) {
SAFE_WRITE_EX(device, ptr[i]);
}
}
void writeVarString(const QString &value, QIODevice *device)
{
quint32 lenTag = value.length() != 4 ? value.length() : 0;
SAFE_WRITE_EX(device, lenTag);
if (!device->write(value.toAscii().data(), value.length())) {
- qWarning() << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
+ warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void writePascalString(const QString &value, QIODevice *device)
{
quint8 lenTag = value.length();
SAFE_WRITE_EX(device, lenTag);
if (!device->write(value.toAscii().data(), value.length())) {
- qWarning() << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
+ warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void writeFixedString(const QString &value, QIODevice *device)
{
KIS_ASSERT_RECOVER_RETURN(value.length() == 4);
if (!device->write(value.toAscii().data(), value.length())) {
- qWarning() << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
+ warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
// Write UUID fetched from the file name or generate
QString getPatternUuidLazy(const KoPattern *pattern)
{
QUuid uuid;
QString patternFileName = pattern->filename();
if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) {
QString strUuid = patternFileName.left(patternFileName.size() - 4);
uuid = QUuid(strUuid);
}
if (uuid.isNull()) {
- qWarning() << "WARNING: Saved pattern doesn't have a UUID, generating...";
- qWarning() << ppVar(patternFileName) << ppVar(pattern->name());
+ warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating...";
+ warnKrita << ppVar(patternFileName) << ppVar(pattern->name());
uuid = QUuid::createUuid();
}
return uuid.toString().mid(1, 36);
}
}
diff --git a/krita/libpsd/asl/kis_asl_xml_parser.cpp b/krita/libpsd/asl/kis_asl_xml_parser.cpp
index 30b93aba9da..36355b86411 100644
--- a/krita/libpsd/asl/kis_asl_xml_parser.cpp
+++ b/krita/libpsd/asl/kis_asl_xml_parser.cpp
@@ -1,556 +1,556 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_xml_parser.h"
#include <boost/function.hpp>
#include <stdexcept>
#include <string>
#include <QDomDocument>
#include <QIODevice>
#include <QBuffer>
#include <QColor>
#include <QHash>
#include <KoColorSpaceRegistry.h>
#include <KoSegmentGradient.h>
#include "kis_dom_utils.h"
#include "kis_debug.h"
#include "psd_utils.h"
#include "psd.h"
#include "compression.h"
#include "kis_asl_object_catcher.h"
namespace Private {
void parseElement(const QDomElement &el,
const QString &parentPath,
KisAslObjectCatcher &catcher);
class CurveObjectCatcher : public KisAslObjectCatcher
{
public:
void addText(const QString &path, const QString &value) {
if (path == "/Nm ") {
m_name = value;
} else {
- qWarning() << "XML (ASL): failed to parse curve object" << path << value;
+ warnKrita << "XML (ASL): failed to parse curve object" << path << value;
}
}
void addPoint(const QString &path, const QPointF &value) {
if (!m_arrayMode) {
- qWarning() << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode);
+ warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode);
}
m_points.append(value);
}
public:
QVector<QPointF> m_points;
QString m_name;
};
QColor parseRGBColorObject(QDomElement parent)
{
QColor color(Qt::black);
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type != "Double") {
- qWarning() << "Unknown color component type:" << ppVar(type) << ppVar(key);
+ warnKrita << "Unknown color component type:" << ppVar(type) << ppVar(key);
return Qt::red;
}
double value = KisDomUtils::Private::stringToDouble(childEl.attribute("value", "0"));
if (key == "Rd ") {
color.setRed(value);
} else if (key == "Grn ") {
color.setGreen(value);
} else if (key == "Bl ") {
color.setBlue(value);
} else {
- qWarning() << "Unknown color key value:" << ppVar(key);
+ warnKrita << "Unknown color key value:" << ppVar(key);
return Qt::red;
}
child = child.nextSibling();
}
return color;
}
void parseColorStopsList(QDomElement parent,
QVector<qreal> &startLocations,
QVector<qreal> &middleOffsets,
QVector<QColor> &colors)
{
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Descriptor" && classId == "Clrt") {
// sorry for naming...
QDomNode child = childEl.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Integer" && key == "Lctn") {
int value = KisDomUtils::Private::stringToInt(childEl.attribute("value", "0"));
startLocations.append(qreal(value) / 4096.0);
} else if (type == "Integer" && key == "Mdpn") {
int value = KisDomUtils::Private::stringToInt(childEl.attribute("value", "0"));
middleOffsets.append(qreal(value) / 100.0);
} else if (type == "Descriptor" && key == "Clr ") {
colors.append(parseRGBColorObject(childEl));
} else if (type == "Enum" && key == "Type") {
QString typeId = childEl.attribute("typeId", "");
if (typeId != "Clry") {
- qWarning() << "WARNING: Invalid typeId of a greadient stop type" << typeId;
+ warnKrita << "WARNING: Invalid typeId of a greadient stop type" << typeId;
}
QString value = childEl.attribute("value", "");
if (value == "BckC" || value == "FrgC") {
- qWarning() << "WARNING: Using foreground/background colors in ASL gradients is not yet supported";
+ warnKrita << "WARNING: Using foreground/background colors in ASL gradients is not yet supported";
}
}
child = child.nextSibling();
}
} else {
- qWarning() << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
+ warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
}
child = child.nextSibling();
}
}
void parseTransparencyStopsList(QDomElement parent,
QVector<qreal> &startLocations,
QVector<qreal> &middleOffsets,
QVector<qreal> &transparencies)
{
QDomNode child = parent.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString classId = childEl.attribute("classId", "");
if (type == "Descriptor" && classId == "TrnS") {
// sorry for naming again...
QDomNode child = childEl.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Integer" && key == "Lctn") {
int value = KisDomUtils::Private::stringToInt(childEl.attribute("value", "0"));
startLocations.append(qreal(value) / 4096.0);
} else if (type == "Integer" && key == "Mdpn") {
int value = KisDomUtils::Private::stringToInt(childEl.attribute("value", "0"));
middleOffsets.append(qreal(value) / 100.0);
} else if (type == "UnitFloat" && key == "Opct") {
QString unit = childEl.attribute("unit", "");
if (unit != "#Prc") {
- qWarning() << "WARNING: Invalid unit of a greadient stop transparency" << unit;
+ warnKrita << "WARNING: Invalid unit of a greadient stop transparency" << unit;
}
qreal value = KisDomUtils::Private::stringToDouble(childEl.attribute("value", "100"));
transparencies.append(value / 100.0);
}
child = child.nextSibling();
}
} else {
- qWarning() << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
+ warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId);
}
child = child.nextSibling();
}
}
inline QString buildPath(const QString &parent, const QString &key) {
return parent + "/" + key;
}
bool tryParseDescriptor(const QDomElement &el,
const QString &path,
const QString &classId,
KisAslObjectCatcher &catcher)
{
bool retval = true;
if (classId == "null") {
catcher.newStyleStarted();
// here we just notify that a new style is started, we haven't
// processed the whole block yet, so return false.
retval = false;
} else if (classId == "RGBC") {
catcher.addColor(path, parseRGBColorObject(el));
} else if (classId == "ShpC") {
CurveObjectCatcher curveCatcher;
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), "", curveCatcher);
child = child.nextSibling();
}
catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points);
} else if (classId == "CrPt") {
QPointF point;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Boolean" && key == "Cnty") {
- qWarning() << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita";
- qWarning() << " " << ppVar(type) << ppVar(key) << ppVar(path);
+ warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita";
+ warnKrita << " " << ppVar(type) << ppVar(key) << ppVar(path);
child = child.nextSibling();
continue;
}
if (type != "Double") {
- qWarning() << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path);
+ warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path);
return false;
}
double value = KisDomUtils::Private::stringToDouble(childEl.attribute("value", "0"));
if (key == "Hrzn") {
point.setX(value);
} else if (key == "Vrtc") {
point.setY(value);
} else {
- qWarning() << "Unknown point key value:" << ppVar(key) << ppVar(path);
+ warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
return false;
}
child = child.nextSibling();
}
catcher.addPoint(path, point);
} else if (classId == "Pnt ") {
QPointF point;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
QString unit = childEl.attribute("unit", "");
if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) {
- qWarning() << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path);
+ warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path);
return false;
}
double value = KisDomUtils::Private::stringToDouble(childEl.attribute("value", "0"));
if (key == "Hrzn") {
point.setX(value);
} else if (key == "Vrtc") {
point.setY(value);
} else {
- qWarning() << "Unknown point key value:" << ppVar(key) << ppVar(path);
+ warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path);
return false;
}
child = child.nextSibling();
}
catcher.addPoint(path, point);
} else if (classId == "KisPattern") {
QByteArray patternData;
QString patternUuid;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Idnt") {
patternUuid = childEl.attribute("value", "");
}
if (type == "KisPatternData" && key == "Data") {
QDomNode dataNode = child.firstChild();
if (!dataNode.isCDATASection()) {
- qWarning() << "WARNING: failed to parse KisPatternData XML section!";
+ warnKrita << "WARNING: failed to parse KisPatternData XML section!";
continue;
}
QDomCDATASection dataSection = dataNode.toCDATASection();
QByteArray data = dataSection.data().toAscii();
data = QByteArray::fromBase64(data);
data = qUncompress(data);
if (data.isEmpty()) {
- qWarning() << "WARNING: failed to parse KisPatternData XML section!";
+ warnKrita << "WARNING: failed to parse KisPatternData XML section!";
continue;
}
patternData = data;
}
child = child.nextSibling();
}
if (!patternUuid.isEmpty() && !patternData.isEmpty()) {
QString fileName = QString("%1.pat").arg(patternUuid);
QScopedPointer<KoPattern> pattern(new KoPattern(fileName));
QBuffer buffer(&patternData);
buffer.open(QIODevice::ReadOnly);
pattern->loadPatFromDevice(&buffer);
catcher.addPattern(path, pattern.data());
} else {
- qWarning() << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid);
+ warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid);
}
} else if (classId == "Ptrn") { // reference to an existing pattern
QString patternUuid;
QString patternName;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Idnt") {
patternUuid = childEl.attribute("value", "");
} else if (type == "Text" && key == "Nm ") {
patternName = childEl.attribute("value", "");
} else {
- qWarning() << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key);
+ warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key);
}
child = child.nextSibling();
}
catcher.addPatternRef(path, patternUuid, patternName);
} else if (classId == "Grdn") {
QString gradientName;
qreal gradientSmoothness = 100.0;
QVector<qreal> startLocations;
QVector<qreal> middleOffsets;
QVector<QColor> colors;
QVector<qreal> transpStartLocations;
QVector<qreal> transpMiddleOffsets;
QVector<qreal> transparencies;
QDomNode child = el.firstChild();
while (!child.isNull()) {
QDomElement childEl = child.toElement();
QString type = childEl.attribute("type", "<unknown>");
QString key = childEl.attribute("key", "");
if (type == "Text" && key == "Nm ") {
gradientName = childEl.attribute("value", "");
} else if (type == "Enum" && key == "GrdF") {
QString typeId = childEl.attribute("typeId", "");
QString value = childEl.attribute("value", "");
if (typeId != "GrdF" || value != "CstS") {
- qWarning() << "WARNING: Unsupported gradient type (porbably, noise-based):" << value;
+ warnKrita << "WARNING: Unsupported gradient type (porbably, noise-based):" << value;
return true;
}
} else if (type == "Double" && key == "Intr") {
double value = KisDomUtils::Private::stringToDouble(childEl.attribute("value", "4096"));
gradientSmoothness = 100.0 * value / 4096.0;
} else if (type == "List" && key == "Clrs") {
parseColorStopsList(childEl, startLocations, middleOffsets, colors);
} else if (type == "List" && key == "Trns") {
parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies);
}
child = child.nextSibling();
}
if (colors.size() < 2) {
- qWarning() << "WARNING: ASL gradient has too few stops" << ppVar(colors.size());
+ warnKrita << "WARNING: ASL gradient has too few stops" << ppVar(colors.size());
}
if (colors.size() != transparencies.size()) {
- qWarning() << "WARNING: ASL gradient has inconsistent number of transparency stops. Dropping transparency..." << ppVar(colors.size()) << ppVar(transparencies.size());
+ warnKrita << "WARNING: ASL gradient has inconsistent number of transparency stops. Dropping transparency..." << ppVar(colors.size()) << ppVar(transparencies.size());
transparencies.resize(colors.size());
for (int i = 0; i < colors.size(); i++) {
transparencies[i] = 1.0;
}
}
QString fileName = gradientName + ".ggr";
QSharedPointer<KoSegmentGradient> gradient(new KoSegmentGradient(fileName));
Q_UNUSED(gradientSmoothness);
gradient->setName(gradientName);
for (int i = 1; i < colors.size(); i++) {
QColor startColor = colors[i-1];
QColor endColor = colors[i];
startColor.setAlphaF(transparencies[i-1]);
endColor.setAlphaF(transparencies[i]);
qreal start = startLocations[i-1];
qreal end = startLocations[i];
qreal middle = start + middleOffsets[i-1] * (end - start);
gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
start, end, middle,
startColor,
endColor);
}
gradient->setValid(true);
catcher.addGradient(path, gradient);
} else {
retval = false;
}
return retval;
}
void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher)
{
KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
QString type = el.attribute("type", "<unknown>");
QString key = el.attribute("key", "");
if (type == "Descriptor") {
QString classId = el.attribute("classId", "<noClassId>");
QString containerName = key.isEmpty() ? classId : key;
QString containerPath = buildPath(parentPath, containerName);
if (!tryParseDescriptor(el, containerPath, classId, catcher)) {
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), containerPath, catcher);
child = child.nextSibling();
}
}
} else if (type == "List") {
catcher.setArrayMode(true);
QString containerName = key;
QString containerPath = buildPath(parentPath, containerName);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), containerPath, catcher);
child = child.nextSibling();
}
catcher.setArrayMode(false);
} else if (type == "Double") {
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
catcher.addDouble(buildPath(parentPath, key), v);
} else if (type == "UnitFloat") {
QString unit = el.attribute("unit", "<unknown>");
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
catcher.addUnitFloat(buildPath(parentPath, key), unit, v);
} else if (type == "Text") {
QString v = el.attribute("value", "");
catcher.addText(buildPath(parentPath, key), v);
} else if (type == "Enum") {
QString v = el.attribute("value", "");
QString typeId = el.attribute("typeId", "<unknown>");
catcher.addEnum(buildPath(parentPath, key), typeId, v);
} else if (type == "Integer") {
int v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
catcher.addInteger(buildPath(parentPath, key), v);
} else if (type == "Boolean") {
int v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
catcher.addBoolean(buildPath(parentPath, key), v);
} else {
- qWarning() << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key);
+ warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key);
}
}
} // namespace
void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher)
{
QDomElement root = doc.documentElement();
if (root.tagName() != "asl") {
return;
}
QDomNode child = root.firstChild();
while (!child.isNull()) {
Private::parseElement(child.toElement(), "", catcher);
child = child.nextSibling();
}
}
diff --git a/krita/libpsd/asl/kis_asl_xml_writer.cpp b/krita/libpsd/asl/kis_asl_xml_writer.cpp
index 44296edc82f..97c876af030 100644
--- a/krita/libpsd/asl/kis_asl_xml_writer.cpp
+++ b/krita/libpsd/asl/kis_asl_xml_writer.cpp
@@ -1,397 +1,397 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_xml_writer.h"
#include <QDomDocument>
#include <QColor>
#include <QPointF>
#include <QUuid>
#include <QBuffer>
#include <KoPattern.h>
#include <KoSegmentGradient.h>
#include <KoStopGradient.h>
#include <cfloat>
#include "kis_dom_utils.h"
#include "kis_asl_writer_utils.h"
struct KisAslXmlWriter::Private
{
QDomDocument document;
QDomElement currentElement;
};
KisAslXmlWriter::KisAslXmlWriter()
: m_d(new Private)
{
QDomElement el = m_d->document.createElement("asl");
m_d->document.appendChild(el);
m_d->currentElement = el;
}
KisAslXmlWriter::~KisAslXmlWriter()
{
}
QDomDocument KisAslXmlWriter::document() const
{
if (m_d->document.documentElement() != m_d->currentElement) {
- qWarning() << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array";
+ warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array";
}
return m_d->document;
}
void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Descriptor");
el.setAttribute("name", name);
el.setAttribute("classId", classId);
m_d->currentElement.appendChild(el);
m_d->currentElement = el;
}
void KisAslXmlWriter::leaveDescriptor()
{
if (!m_d->currentElement.parentNode().toElement().isNull()) {
m_d->currentElement = m_d->currentElement.parentNode().toElement();
} else {
- qWarning() << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor";
+ warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor";
}
}
void KisAslXmlWriter::enterList(const QString &key)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "List");
m_d->currentElement.appendChild(el);
m_d->currentElement = el;
}
void KisAslXmlWriter::leaveList()
{
if (!m_d->currentElement.parentNode().toElement().isNull()) {
m_d->currentElement = m_d->currentElement.parentNode().toElement();
} else {
- qWarning() << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list";
+ warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list";
}
}
void KisAslXmlWriter::writeDouble(const QString &key, double value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Double");
el.setAttribute("value", KisDomUtils::Private::numberToString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeInteger(const QString &key, int value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Integer");
el.setAttribute("value", KisDomUtils::Private::numberToString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Enum");
el.setAttribute("typeId", typeId);
el.setAttribute("value", value);
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "UnitFloat");
el.setAttribute("unit", unit);
el.setAttribute("value", KisDomUtils::Private::numberToString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeText(const QString &key, const QString &value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Text");
el.setAttribute("value", value);
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeBoolean(const QString &key, bool value)
{
QDomElement el = m_d->document.createElement("node");
if (!key.isEmpty()) {
el.setAttribute("key", key);
}
el.setAttribute("type", "Boolean");
el.setAttribute("value", KisDomUtils::Private::numberToString(value));
m_d->currentElement.appendChild(el);
}
void KisAslXmlWriter::writeColor(const QString &key, const QColor &value)
{
enterDescriptor(key, "", "RGBC");
writeDouble("Rd ", value.red());
writeDouble("Grn ", value.green());
writeDouble("Bl ", value.blue());
leaveDescriptor();
}
void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "CrPt");
writeDouble("Hrzn", value.x());
writeDouble("Vrtc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "Pnt ");
writeDouble("Hrzn", value.x());
writeDouble("Vrtc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value)
{
enterDescriptor(key, "", "Pnt ");
writeUnitFloat("Hrzn", "#Prc", value.x());
writeUnitFloat("Vrtc", "#Prc", value.y());
leaveDescriptor();
}
void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector<QPointF> &points)
{
enterDescriptor(key, "", "ShpC");
writeText("Nm ", name);
enterList("Crv ");
foreach (const QPointF &pt, points) {
writePoint("", pt);
}
leaveList();
leaveDescriptor();
}
QString KisAslXmlWriter::writePattern(const QString &key, const KoPattern *pattern)
{
enterDescriptor(key, "", "KisPattern");
writeText("Nm ", pattern->name());
QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
writeText("Idnt", uuid);
// Write pattern data
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
pattern->savePatToDevice(&buffer);
QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64());
QDomElement dataElement = m_d->document.createElement("node");
dataElement.setAttribute("type", "KisPatternData");
dataElement.setAttribute("key", "Data");
dataElement.appendChild(dataSection);
m_d->currentElement.appendChild(dataElement);
leaveDescriptor();
return uuid;
}
void KisAslXmlWriter::writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid)
{
enterDescriptor(key, "", "Ptrn");
writeText("Nm ", pattern->name());
writeText("Idnt", uuid);
leaveDescriptor();
}
void KisAslXmlWriter::writeGradientImpl(const QString &key,
const QString &name,
QVector<QColor> colors,
QVector<qreal> transparencies,
QVector<qreal> positions,
QVector<qreal> middleOffsets)
{
enterDescriptor(key, "Gradient", "Grdn");
writeText("Nm ", name);
writeEnum("GrdF", "GrdF", "CstS");
writeDouble("Intr", 4096);
enterList("Clrs");
for (int i = 0; i < colors.size(); i++) {
enterDescriptor("", "", "Clrt");
writeColor("Clr ", colors[i]);
writeEnum("Type", "Clry", "UsrS"); // NOTE: we do not support BG/FG color tags
writeInteger("Lctn", positions[i] * 4096.0);
writeInteger("Mdpn", middleOffsets[i] * 100.0);
leaveDescriptor();
};
leaveList();
enterList("Trns");
for (int i = 0; i < colors.size(); i++) {
enterDescriptor("", "", "TrnS");
writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0);
writeInteger("Lctn", positions[i] * 4096.0);
writeInteger("Mdpn", middleOffsets[i] * 100.0);
leaveDescriptor();
};
leaveList();
leaveDescriptor();
}
void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient)
{
const QList<KoGradientSegment *>&segments = gradient->segments();
QVector<QColor> colors;
QVector<qreal> transparencies;
QVector<qreal> positions;
QVector<qreal> middleOffsets;
foreach (const KoGradientSegment *seg, segments) {
const qreal start = seg->startOffset();
const qreal end = seg->endOffset();
const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5;
QColor color = seg->startColor().toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << start;
middleOffsets << mid;
}
// last segment
{
const KoGradientSegment *lastSeg = segments.last();
QColor color = lastSeg->endColor().toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << lastSeg->endOffset();
middleOffsets << 0.5;
}
writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets);
}
void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient *gradient)
{
QVector<QColor> colors;
QVector<qreal> transparencies;
QVector<qreal> positions;
QVector<qreal> middleOffsets;
foreach (const KoGradientStop &stop, gradient->stops()) {
QColor color = stop.second.toQColor();
qreal transparency = color.alphaF();
color.setAlphaF(1.0);
colors << color;
transparencies << transparency;
positions << stop.first;
middleOffsets << 0.5;
}
writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets);
}
diff --git a/krita/libpsd/asl/kis_offset_keeper.h b/krita/libpsd/asl/kis_offset_keeper.h
index 6b4308736e0..eb5117d29c5 100644
--- a/krita/libpsd/asl/kis_offset_keeper.h
+++ b/krita/libpsd/asl/kis_offset_keeper.h
@@ -1,52 +1,52 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_OFFSET_KEEPER_H
#define __KIS_OFFSET_KEEPER_H
-#include <QDebug>
+#include <kis_debug.h>
#include <QIODevice>
/**
* Restore the offset of the io device on exit from the current
* namespace
*/
class KisOffsetKeeper
{
public:
KisOffsetKeeper(QIODevice *device)
: m_device(device)
{
m_expectedPos = m_device->pos();
}
~KisOffsetKeeper() {
if (m_device->pos() != m_expectedPos) {
m_device->seek(m_expectedPos);
}
}
private:
QIODevice *m_device;
qint64 m_expectedPos;
};
#endif /* __KIS_OFFSET_KEEPER_H */
diff --git a/krita/libpsd/asl/kis_offset_on_exit_verifier.h b/krita/libpsd/asl/kis_offset_on_exit_verifier.h
index 03d0895c4f6..ef6b9363dae 100644
--- a/krita/libpsd/asl/kis_offset_on_exit_verifier.h
+++ b/krita/libpsd/asl/kis_offset_on_exit_verifier.h
@@ -1,81 +1,81 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_OFFSET_ON_EXIT_VERIFIER_H
#define __KIS_OFFSET_ON_EXIT_VERIFIER_H
-#include <QDebug>
+#include <kis_debug.h>
#include <QString>
#include <QIODevice>
//#define DEBUG_OFFSET_ON_EXIT
/**
* Check if the position of \p device has moved further by
* \p expectedOffset and correct it if needed. It also issues
* a warning if needed.
*/
class KisOffsetOnExitVerifier
{
public:
KisOffsetOnExitVerifier(QIODevice *device,
qint64 expectedOffset,
int maxPadding,
const QString &objectName = "",
const QString &domain = "")
: m_device(device),
m_maxPadding(maxPadding),
m_domain(domain),
m_objectName(objectName)
{
m_expectedPos = m_device->pos() + expectedOffset;
}
~KisOffsetOnExitVerifier() {
if (m_device->pos() < m_expectedPos - m_maxPadding ||
m_device->pos() > m_expectedPos) {
#ifdef DEBUG_OFFSET_ON_EXIT
QString msg =
QString("Incorrect offset on exit %1, expected %2!")
.arg(m_device->pos())
.arg(m_expectedPos);
- qWarning() << "*** |" << m_objectName << msg;
- qWarning() << " |" << m_domain;
+ warnKrita << "*** |" << m_objectName << msg;
+ warnKrita << " |" << m_domain;
#endif /* DEBUG_OFFSET_ON_EXIT */
m_device->seek(m_expectedPos);
}
}
private:
QIODevice *m_device;
int m_maxPadding;
qint64 m_expectedPos;
QString m_domain;
QString m_objectName;
};
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding) KisOffsetOnExitVerifier name(device, expectedOffset, maxPadding, QString(#name), QString(__FILE__) + ":" + QString::number(__LINE__))
#endif /* __KIS_OFFSET_ON_EXIT_VERIFIER_H */
diff --git a/krita/libpsd/psd.cpp b/krita/libpsd/psd.cpp
index 541a91c39f4..8f475803aa5 100644
--- a/krita/libpsd/psd.cpp
+++ b/krita/libpsd/psd.cpp
@@ -1,212 +1,212 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QPoint>
#include <KoColorModelStandardIds.h>
#include <KoCompositeOpRegistry.h>
#include "kis_global.h"
QPair<QString, QString> psd_colormode_to_colormodelid(psd_color_mode colormode, quint16 channelDepth)
{
QPair<QString, QString> colorSpaceId;
switch(colormode) {
case(Bitmap):
case(Indexed):
case(MultiChannel):
case(RGB):
colorSpaceId.first = RGBAColorModelID.id();
break;
case(CMYK):
colorSpaceId.first = CMYKAColorModelID.id();
break;
case(Grayscale):
colorSpaceId.first = GrayAColorModelID.id();
break;
case(DuoTone):
colorSpaceId.first = GrayAColorModelID.id();
break;
case(Lab):
colorSpaceId.first = LABAColorModelID.id();
break;
default:
return colorSpaceId;
}
switch(channelDepth) {
case(1):
case(8):
colorSpaceId.second = Integer8BitsColorDepthID.id();
break;
case(16):
colorSpaceId.second = Integer16BitsColorDepthID.id();
break;
case(32):
colorSpaceId.second = Float32BitsColorDepthID.id();
break;
default:
break;
}
return colorSpaceId;
}
QString psd_blendmode_to_composite_op(const QString& blendmode)
{
// 'pass' = pass through
if (blendmode == "pass") return COMPOSITE_PASS_THROUGH;
// 'norm' = normal
if (blendmode == "norm") return COMPOSITE_OVER;
// 'diss' = dissolve
if (blendmode == "diss") return COMPOSITE_DISSOLVE;
// 'dark' = darken
if (blendmode == "dark") return COMPOSITE_DARKEN;
// 'mul ' = multiply
if (blendmode == "mul ") return COMPOSITE_MULT;
// 'idiv' = color burn
if (blendmode == "idiv") return COMPOSITE_BURN;
// 'lbrn' = linear burn
if (blendmode == "lbrn") return COMPOSITE_LINEAR_BURN;
// 'dkCl' = darker color
if (blendmode == "dkCl") return COMPOSITE_DARKER_COLOR;
// 'lite' = lighten
if (blendmode == "lite") return COMPOSITE_LIGHTEN;
// 'scrn' = screen
if (blendmode == "scrn") return COMPOSITE_SCREEN;
// 'div ' = color dodge
if (blendmode == "div ") return COMPOSITE_DODGE;
// 'lddg' = linear dodge
if (blendmode == "lddg") return COMPOSITE_LINEAR_DODGE;
// 'lgCl' = lighter color
if (blendmode == "lgCl") return COMPOSITE_LIGHTER_COLOR;
// 'over' = overlay
if (blendmode == "over") return COMPOSITE_OVERLAY;
// 'sLit' = soft light
if (blendmode == "sLit") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
// 'hLit' = hard light
if (blendmode == "hLit") return COMPOSITE_HARD_LIGHT;
// 'vLit' = vivid light
if (blendmode == "vLit") return COMPOSITE_VIVID_LIGHT;
// 'lLit' = linear light
if (blendmode == "lLit") return COMPOSITE_LINEAR_LIGHT;
// 'pLit' = pin light
if (blendmode == "pLit") return COMPOSITE_PIN_LIGHT;
// 'hMix' = hard mix
if (blendmode == "hMix") return COMPOSITE_HARD_MIX;
// 'diff' = difference
if (blendmode == "diff") return COMPOSITE_DIFF;
// 'smud' = exclusion
if (blendmode == "smud") return COMPOSITE_EXCLUSION;
// 'fsub' = subtract
if (blendmode == "fsub") return COMPOSITE_SUBTRACT;
// 'fdiv' = divide
if (blendmode == "fdiv") return COMPOSITE_DIVIDE;
// 'hue ' = hue
if (blendmode == "hue ") return COMPOSITE_HUE;
// 'sat ' = saturation
if (blendmode == "sat ") return COMPOSITE_SATURATION;
// 'colr' = color
if (blendmode == "colr") return COMPOSITE_COLOR;
// 'lum ' = luminosity
if (blendmode == "lum ") return COMPOSITE_LUMINIZE;
- qDebug() << "Unknown blendmode:" << blendmode << ". Returning Normal";
+ dbgKrita << "Unknown blendmode:" << blendmode << ". Returning Normal";
return COMPOSITE_OVER;
}
QString composite_op_to_psd_blendmode(const QString& compositeop)
{
// 'pass' = pass through
if (compositeop == COMPOSITE_PASS_THROUGH) return "pass";
// 'norm' = normal
if (compositeop == COMPOSITE_OVER) return "norm";
// 'diss' = dissolve
if (compositeop == COMPOSITE_DISSOLVE) return "diss";
// 'dark' = darken
if (compositeop == COMPOSITE_DARKEN) return "dark";
// 'mul ' = multiply
if (compositeop == COMPOSITE_MULT) return "mul ";
// 'idiv' = color burn
if (compositeop == COMPOSITE_BURN ) return "idiv";
// 'lbrn' = linear burn
if (compositeop == COMPOSITE_LINEAR_BURN) return "lbrn";
// 'dkCl' = darker color
if (compositeop == COMPOSITE_DARKER_COLOR) return "dkCl";
// 'lite' = lighten
if (compositeop == COMPOSITE_LIGHTEN) return "lite";
// 'scrn' = screen
if (compositeop == COMPOSITE_SCREEN) return "scrn";
// 'div ' = color dodge
if (compositeop == COMPOSITE_DODGE) return "div ";
// 'lddg' = linear dodge
if (compositeop == COMPOSITE_LINEAR_DODGE ) return "lddg";
// 'lgCl' = lighter color
if (compositeop == COMPOSITE_LIGHTER_COLOR) return "lgCl";
// 'over' = overlay
if (compositeop == COMPOSITE_OVERLAY) return "over";
// 'sLit' = soft light
if (compositeop == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) return "sLit";
if (compositeop == COMPOSITE_SOFT_LIGHT_SVG) return "sLit";
// 'hLit' = hard light
if (compositeop == COMPOSITE_HARD_LIGHT) return "hLit";
// 'vLit' = vivid light
if (compositeop == COMPOSITE_VIVID_LIGHT) return "vLit";
// 'lLit' = linear light
if (compositeop == COMPOSITE_LINEAR_LIGHT) return "lLit";
// 'pLit' = pin light
if (compositeop == COMPOSITE_PIN_LIGHT) return "pLit";
// 'hMix' = hard mix
if (compositeop == COMPOSITE_HARD_MIX) return "hMix";
// 'diff' = difference
if (compositeop == COMPOSITE_DIFF) return "diff";
// 'smud' = exclusion
if (compositeop == COMPOSITE_EXCLUSION) return "smud";
// 'fsub' = subtract
if (compositeop == COMPOSITE_SUBTRACT) return "fsub";
// 'fdiv' = divide
if (compositeop == COMPOSITE_DIVIDE) return "fdiv";
// 'hue ' = hue
if (compositeop == COMPOSITE_HUE) return "hue ";
// 'sat ' = saturation
if (compositeop == COMPOSITE_SATURATION) return "sat ";
// 'colr' = color
if (compositeop == COMPOSITE_COLOR) return "colr";
// 'lum ' = luminosity
if (compositeop == COMPOSITE_LUMINIZE) return "lum ";
- qDebug() << "Krita blending mode" << compositeop << "does not exist in Photoshop, returning Normal";
+ dbgKrita << "Krita blending mode" << compositeop << "does not exist in Photoshop, returning Normal";
return "norm";
}
QPoint psd_layer_effects_shadow_base::calculateOffset(const psd_layer_effects_context *context) const
{
Q_UNUSED(context);
qint32 angle = this->m_angle;
qint32 distanceX = -qRound(this->m_distance * cos(kisDegreesToRadians(qreal(angle))));
qint32 distanceY = qRound(this->m_distance * sin(kisDegreesToRadians(qreal(angle))));
return QPoint(distanceX, distanceY);
}
diff --git a/krita/libpsd/psd.h b/krita/libpsd/psd.h
index 584717baf79..c4c3fb183a5 100644
--- a/krita/libpsd/psd.h
+++ b/krita/libpsd/psd.h
@@ -1,1157 +1,1157 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Constants and defines taken from gimp and psdparse
*/
#ifndef PSD_H
#define PSD_H
#include <QPair>
#include <QString>
#include <QColor>
#include <QVector>
#include <KoColorModelStandardIds.h>
#include <KoCompositeOpRegistry.h>
#include <KoAbstractGradient.h>
#include "kritapsd_export.h"
-#include "kis_debug.h"
+#include "kis_types.h"
class KoPattern;
const int MAX_CHANNELS = 56;
typedef qint32 Fixed; /* Represents a fixed point implied decimal */
/**
* Image color/depth modes
*/
enum psd_color_mode {
Bitmap = 0,
Grayscale=1,
Indexed=2,
RGB=3,
CMYK=4,
MultiChannel=7,
DuoTone=8,
Lab=9,
Gray16,
RGB48,
Lab48,
CMYK64,
DeepMultichannel,
Duotone16,
UNKNOWN = 9000
};
/**
* Color samplers, apparently distict from PSDColormode
*/
namespace psd_color_sampler {
enum PSDColorSamplers {
RGB,
HSB,
CMYK,
PANTONE, // LAB
FOCOLTONE, // CMYK
TRUMATCH, // CMYK
TOYO, // LAB
LAB,
GRAYSCALE,
HKS, // CMYK
DIC, // LAB
TOTAL_INK,
MONITOR_RGB,
DUOTONE,
OPACITY,
ANPA = 3000 // LAB
};
}
// EFFECTS
enum psd_gradient_style {
psd_gradient_style_linear, // 'Lnr '
psd_gradient_style_radial, // 'Rdl '
psd_gradient_style_angle, // 'Angl'
psd_gradient_style_reflected, // 'Rflc'
psd_gradient_style_diamond // 'Dmnd'
};
enum psd_color_stop_type {
psd_color_stop_type_foreground_color, // 'FrgC'
psd_color_stop_type_background_Color, // 'BckC'
psd_color_stop_type_user_stop // 'UsrS'
};
enum psd_technique_type {
psd_technique_softer,
psd_technique_precise,
psd_technique_slope_limit,
};
enum psd_stroke_position {
psd_stroke_outside,
psd_stroke_inside,
psd_stroke_center
};
enum psd_fill_type {
psd_fill_solid_color,
psd_fill_gradient,
psd_fill_pattern,
};
enum psd_glow_source {
psd_glow_center,
psd_glow_edge,
};
enum psd_bevel_style {
psd_bevel_outer_bevel,
psd_bevel_inner_bevel,
psd_bevel_emboss,
psd_bevel_pillow_emboss,
psd_bevel_stroke_emboss,
};
enum psd_direction {
psd_direction_up,
psd_direction_down
};
enum psd_section_type {
psd_other = 0,
psd_open_folder,
psd_closed_folder,
psd_bounding_divider
};
// GRADIENT MAP
// Each color stop
struct psd_gradient_color_stop
{
qint32 location; // Location of color stop
qint32 midpoint; // Midpoint of color stop
QColor actual_color;
psd_color_stop_type color_stop_type;
};
// Each transparency stop
struct psd_gradient_transparency_stop
{
qint32 location; // Location of transparency stop
qint32 midpoint; // Midpoint of transparency stop
qint8 opacity; // Opacity of transparency stop
};
// Gradient settings (Photoshop 6.0)
struct psd_layer_gradient_map
{
bool reverse; // Is gradient reverse
bool dithered; // Is gradient dithered
qint32 name_length;
quint16 *name; // Name of the gradient: Unicode string, padded
qint8 number_color_stops; // Number of color stops to follow
psd_gradient_color_stop * color_stop;
qint8 number_transparency_stops;// Number of transparency stops to follow
psd_gradient_transparency_stop * transparency_stop;
qint8 expansion_count; // Expansion count ( = 2 for Photoshop 6.0)
qint8 interpolation; // Interpolation if length above is non-zero
qint8 length; // Length (= 32 for Photoshop 6.0)
qint8 mode; // Mode for this gradient
qint32 random_number_seed; // Random number seed
qint8 showing_transparency_flag;// Flag for showing transparency
qint8 using_vector_color_flag;// Flag for using vector color
qint32 roughness_factor; // Roughness factor
QColor min_color;
QColor max_color;
QColor lookup_table[256];
};
struct psd_gradient_color {
qint32 smoothness;
qint32 name_length;
quint16 * name; // Name of the gradient: Unicode string, padded
qint8 number_color_stops; // Number of color stops to follow
psd_gradient_color_stop * color_stop;
qint8 number_transparency_stops;// Number of transparency stops to follow
psd_gradient_transparency_stop *transparency_stop;
};
struct psd_pattern {
psd_color_mode color_mode; // The image mode of the file.
quint8 height; // Point: vertical, 2 bytes and horizontal, 2 bytes
quint8 width;
QString name;
QString uuid;
qint32 version;
quint8 top; // Rectangle: top, left, bottom, right
quint8 left;
quint8 bottom;
quint8 right;
qint32 max_channel; // Max channels
qint32 channel_number;
QVector<QRgb> color_table;
};
struct psd_layer_effects_context {
psd_layer_effects_context()
: keep_original(false)
{
}
bool keep_original;
};
#define PSD_LOOKUP_TABLE_SIZE 256
// dsdw, isdw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203
class KRITAPSD_EXPORT psd_layer_effects_shadow_base {
public:
psd_layer_effects_shadow_base()
: m_invertsSelection(false)
, m_edgeHidden(true)
, m_effectEnabled(false)
, m_blendMode(COMPOSITE_MULT)
, m_color(Qt::black)
, m_nativeColor(Qt::black)
, m_opacity(75)
, m_angle(120)
, m_useGlobalLight(true)
, m_distance(21)
, m_spread(0)
, m_size(21)
, m_antiAliased(0)
, m_noise(0)
, m_knocksOut(false)
, m_fillType(psd_fill_solid_color)
, m_technique(psd_technique_softer)
, m_range(100)
, m_jitter(0)
, m_gradient(0)
{
for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) {
m_contourLookupTable[i] = i;
}
}
virtual ~psd_layer_effects_shadow_base() {
}
QPoint calculateOffset(const psd_layer_effects_context *context) const;
void setEffectEnabled(bool value) {
m_effectEnabled = value;
}
bool effectEnabled() const {
return m_effectEnabled;
}
QString blendMode() const {
return m_blendMode;
}
QColor color() const {
return m_color;
}
QColor nativeColor() const {
return m_nativeColor;
}
qint32 opacity() const {
return m_opacity;
}
qint32 angle() const {
return m_angle;
}
bool useGlobalLight() const {
return m_useGlobalLight;
}
qint32 distance() const {
return m_distance;
}
qint32 spread() const {
return m_spread;
}
qint32 size() const {
return m_size;
}
const quint8* contourLookupTable() const {
return m_contourLookupTable;
}
bool antiAliased() const {
return m_antiAliased;
}
qint32 noise() const {
return m_noise;
}
bool knocksOut() const {
return m_knocksOut;
}
bool invertsSelection() const {
return m_invertsSelection;
}
bool edgeHidden() const {
return m_edgeHidden;
}
psd_fill_type fillType() const {
return m_fillType;
}
psd_technique_type technique() const {
return m_technique;
}
qint32 range() const {
return m_range;
}
qint32 jitter() const {
return m_jitter;
}
KoAbstractGradientSP gradient() const {
return m_gradient;
}
public:
void setBlendMode(QString value) {
m_blendMode = value;
}
void setColor(QColor value) {
m_color = value;
}
void setNativeColor(QColor value) {
m_nativeColor = value;
}
void setOpacity(qint32 value) {
m_opacity = value;
}
void setAngle(qint32 value) {
m_angle = value;
}
void setUseGlobalLight(bool value) {
m_useGlobalLight = value;
}
void setDistance(qint32 value) {
m_distance = value;
}
void setSpread(qint32 value) {
m_spread = value;
}
void setSize(qint32 value) {
m_size = value;
}
void setContourLookupTable(const quint8* value) {
memcpy(m_contourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8));
}
void setAntiAliased(bool value) {
m_antiAliased = value;
}
void setNoise(qint32 value) {
m_noise = value;
}
void setKnocksOut(bool value) {
m_knocksOut = value;
}
void setInvertsSelection(bool value) {
m_invertsSelection = value;
}
void setEdgeHidden(bool value) {
m_edgeHidden = value;
}
void setFillType(psd_fill_type value) {
m_fillType = value;
}
void setTechnique(psd_technique_type value) {
m_technique = value;
}
void setRange(qint32 value) {
m_range = value;
}
void setJitter(qint32 value) {
m_jitter = value;
}
void setGradient(KoAbstractGradientSP value) {
m_gradient = value;
}
private:
// internal
bool m_invertsSelection;
bool m_edgeHidden;
private:
bool m_effectEnabled; // Effect enabled
QString m_blendMode; // already in Krita format!
QColor m_color;
QColor m_nativeColor;
qint32 m_opacity; // Opacity as a percent (0...100)
qint32 m_angle; // Angle in degrees
bool m_useGlobalLight; // Use this angle in all of the layer effects
qint32 m_distance; // Distance in pixels
qint32 m_spread; // Intensity as a percent
qint32 m_size; // Blur value in pixels
quint8 m_contourLookupTable[PSD_LOOKUP_TABLE_SIZE];
bool m_antiAliased;
qint32 m_noise;
bool m_knocksOut;
// for Outer/Inner Glow
psd_fill_type m_fillType;
psd_technique_type m_technique;
qint32 m_range;
qint32 m_jitter;
KoAbstractGradientSP m_gradient;
};
class KRITAPSD_EXPORT psd_layer_effects_shadow_common : public psd_layer_effects_shadow_base
{
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setUseGlobalLight;
// using psd_layer_effects_shadow_base::setDistance;
// using psd_layer_effects_shadow_base::setSpread;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
// using psd_layer_effects_shadow_base::setNoise;
};
class KRITAPSD_EXPORT psd_layer_effects_drop_shadow : public psd_layer_effects_shadow_common
{
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
//using psd_layer_effects_shadow_base::setKnocksOut;
};
// isdw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_22203
class KRITAPSD_EXPORT psd_layer_effects_inner_shadow : public psd_layer_effects_shadow_common
{
public:
psd_layer_effects_inner_shadow() {
setKnocksOut(true);
setInvertsSelection(true);
setEdgeHidden(false);
}
};
class KRITAPSD_EXPORT psd_layer_effects_glow_common : public psd_layer_effects_shadow_base
{
public:
psd_layer_effects_glow_common() {
setKnocksOut(true);
setDistance(0);
setBlendMode(COMPOSITE_LINEAR_DODGE);
setColor(Qt::white);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// using psd_layer_effects_shadow_base::setSpread;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
// using psd_layer_effects_shadow_base::setNoise;
// using psd_layer_effects_shadow_base::setFillType;
// using psd_layer_effects_shadow_base::setTechnique;
// using psd_layer_effects_shadow_base::setRange;
// using psd_layer_effects_shadow_base::setJitter;
// using psd_layer_effects_shadow_base::setGradient;
};
// oglw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_25738
class KRITAPSD_EXPORT psd_layer_effects_outer_glow : public psd_layer_effects_glow_common
{
};
// iglw: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_27692
class KRITAPSD_EXPORT psd_layer_effects_inner_glow : public psd_layer_effects_glow_common
{
public:
psd_layer_effects_inner_glow()
: m_source(psd_glow_edge) {
setInvertsSelection(true);
setEdgeHidden(false);
}
psd_glow_source source() const {
return m_source;
}
void setSource(psd_glow_source value) {
m_source = value;
}
private:
psd_glow_source m_source;
};
struct psd_layer_effects_satin : public psd_layer_effects_shadow_base
{
psd_layer_effects_satin() {
setInvert(false);
setUseGlobalLight(false);
setDistance(8);
setSize(7);
setSpread(0);
setKnocksOut(true);
setEdgeHidden(false);
setBlendMode(COMPOSITE_LINEAR_BURN);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setOpacity;
// // NOTE: no global light setting explicitly!
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setDistance;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
bool invert() const {
return m_invert;
}
void setInvert(bool value) {
m_invert = value;
}
private:
bool m_invert;
};
struct psd_pattern_info {
qint32 name_length;
quint16 * name;
quint8 identifier[256];
};
// bevl: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_31889
struct psd_layer_effects_bevel_emboss : public psd_layer_effects_shadow_base
{
psd_layer_effects_bevel_emboss()
: m_style(psd_bevel_inner_bevel),
m_technique(psd_technique_softer),
m_depth(100),
m_direction(psd_direction_up),
m_soften(0),
m_altitude(30),
m_glossAntiAliased(false),
m_highlightBlendMode(COMPOSITE_SCREEN),
m_highlightColor(Qt::white),
m_highlightOpacity(75),
m_shadowBlendMode(COMPOSITE_MULT),
m_shadowColor(Qt::black),
m_shadowOpacity(75),
m_contourEnabled(false),
m_contourRange(100),
m_textureEnabled(false),
m_texturePattern(0),
m_textureScale(100),
m_textureDepth(100),
m_textureInvert(false),
m_textureAlignWithLayer(true),
m_textureHorizontalPhase(0),
m_textureVerticalPhase(0)
{
for(int i = 0; i < PSD_LOOKUP_TABLE_SIZE; ++i) {
m_glossContourLookupTable[i] = i;
}
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_shadow_base::setUseGlobalLight;
// using psd_layer_effects_shadow_base::setContourLookupTable;
// using psd_layer_effects_shadow_base::setAntiAliased;
psd_bevel_style style() const {
return m_style;
}
void setStyle(psd_bevel_style value) {
m_style = value;
}
psd_technique_type technique() const {
return m_technique;
}
void setTechnique(psd_technique_type value) {
m_technique = value;
}
int depth() const {
return m_depth;
}
void setDepth(int value) {
m_depth = value;
}
psd_direction direction() const {
return m_direction;
}
void setDirection(psd_direction value) {
m_direction = value;
}
int soften() const {
return m_soften;
}
void setSoften(int value) {
m_soften = value;
}
int altitude() const {
return m_altitude;
}
void setAltitude(int value) {
m_altitude = value;
}
const quint8* glossContourLookupTable() const {
return m_glossContourLookupTable;
}
void setGlossContourLookupTable(const quint8 *value) {
memcpy(m_glossContourLookupTable, value, PSD_LOOKUP_TABLE_SIZE * sizeof(quint8));
}
bool glossAntiAliased() const {
return m_glossAntiAliased;
}
void setGlossAntiAliased(bool value) {
m_glossAntiAliased = value;
}
QString highlightBlendMode() const {
return m_highlightBlendMode;
}
void setHighlightBlendMode(QString value) {
m_highlightBlendMode = value;
}
QColor highlightColor() const {
return m_highlightColor;
}
void setHighlightColor(QColor value) {
m_highlightColor = value;
}
qint32 highlightOpacity() const {
return m_highlightOpacity;
}
void setHighlightOpacity(qint32 value) {
m_highlightOpacity = value;
}
QString shadowBlendMode() const {
return m_shadowBlendMode;
}
void setShadowBlendMode(QString value) {
m_shadowBlendMode = value;
}
QColor shadowColor() const {
return m_shadowColor;
}
void setShadowColor(QColor value) {
m_shadowColor = value;
}
qint32 shadowOpacity() const {
return m_shadowOpacity;
}
void setShadowOpacity(qint32 value) {
m_shadowOpacity = value;
}
bool contourEnabled() const {
return m_contourEnabled;
}
void setContourEnabled(bool value) {
m_contourEnabled = value;
}
int contourRange() const {
return m_contourRange;
}
void setContourRange(int value) {
m_contourRange = value;
}
bool textureEnabled() const {
return m_textureEnabled;
}
void setTextureEnabled(bool value) {
m_textureEnabled = value;
}
KoPattern* texturePattern() const {
return m_texturePattern;
}
void setTexturePattern(KoPattern *value) {
m_texturePattern = value;
}
int textureScale() const {
return m_textureScale;
}
void setTextureScale(int value) {
m_textureScale = value;
}
int textureDepth() const {
return m_textureDepth;
}
void setTextureDepth(int value) {
m_textureDepth = value;
}
bool textureInvert() const {
return m_textureInvert;
}
void setTextureInvert(bool value) {
m_textureInvert = value;
}
bool textureAlignWithLayer() const {
return m_textureAlignWithLayer;
}
void setTextureAlignWithLayer(bool value) {
m_textureAlignWithLayer = value;
}
void setTexturePhase(const QPointF &phase) {
m_textureHorizontalPhase = phase.x();
m_textureVerticalPhase = phase.y();
}
QPointF texturePhase() const {
return QPointF(m_textureHorizontalPhase, m_textureVerticalPhase);
}
int textureHorizontalPhase() const {
return m_textureHorizontalPhase;
}
void setTextureHorizontalPhase(int value) {
m_textureHorizontalPhase = value;
}
int textureVerticalPhase() const {
return m_textureVerticalPhase;
}
void setTextureVerticalPhase(int value) {
m_textureVerticalPhase = value;
}
private:
psd_bevel_style m_style;
psd_technique_type m_technique;
int m_depth;
psd_direction m_direction; // Up or down
int m_soften; // Blur value in pixels.
int m_altitude;
quint8 m_glossContourLookupTable[256];
bool m_glossAntiAliased;
QString m_highlightBlendMode; // already in Krita format
QColor m_highlightColor;
qint32 m_highlightOpacity; // Hightlight opacity as a percent
QString m_shadowBlendMode; // already in Krita format
QColor m_shadowColor;
qint32 m_shadowOpacity; // Shadow opacity as a percent
bool m_contourEnabled;
int m_contourRange;
bool m_textureEnabled;
KoPattern *m_texturePattern;
int m_textureScale;
int m_textureDepth;
bool m_textureInvert;
bool m_textureAlignWithLayer;
int m_textureHorizontalPhase; // 0..100%
int m_textureVerticalPhase; // 0..100%
};
struct psd_layer_effects_overlay_base : public psd_layer_effects_shadow_base
{
psd_layer_effects_overlay_base()
: m_scale(100),
m_alignWithLayer(true),
m_reverse(false),
m_style(psd_gradient_style_linear),
m_gradientXOffset(0),
m_gradientYOffset(0),
m_pattern(0),
m_horizontalPhase(0),
m_verticalPhase(0)
{
setUseGlobalLight(false);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setBlendMode;
// using psd_layer_effects_shadow_base::setOpacity;
int scale() const {
return m_scale;
}
bool alignWithLayer() const {
return m_alignWithLayer;
}
bool reverse() const {
return m_reverse;
}
psd_gradient_style style() const {
return m_style;
}
int gradientXOffset() const {
return m_gradientXOffset;
}
int gradientYOffset() const {
return m_gradientYOffset;
}
KoPattern* pattern() const {
return m_pattern;
}
int horizontalPhase() const {
return m_horizontalPhase;
}
int verticalPhase() const {
return m_verticalPhase;
}
// refactor that
public:
void setScale(int value) {
m_scale = value;
}
void setAlignWithLayer(bool value) {
m_alignWithLayer = value;
}
void setReverse(bool value) {
m_reverse = value;
}
void setStyle(psd_gradient_style value) {
m_style = value;
}
void setGradientOffset(const QPointF &pt) {
m_gradientXOffset = qRound(pt.x());
m_gradientYOffset = qRound(pt.y());
}
QPointF gradientOffset() const {
return QPointF(m_gradientXOffset, m_gradientYOffset);
}
void setPattern(KoPattern *value) {
m_pattern = value;
}
void setPatternPhase(const QPointF &phase) {
m_horizontalPhase = phase.x();
m_verticalPhase = phase.y();
}
QPointF patternPhase() const {
return QPointF(m_horizontalPhase, m_verticalPhase);
}
private:
// Gradient+Pattern
int m_scale;
bool m_alignWithLayer;
// Gradient
bool m_reverse;
psd_gradient_style m_style;
int m_gradientXOffset; // 0..100%
int m_gradientYOffset; // 0..100%
// Pattern
KoPattern *m_pattern;
int m_horizontalPhase; // 0..100%
int m_verticalPhase; // 0..100%
protected:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// must be called in the derived classes' c-tor
// using psd_layer_effects_shadow_base::setFillType;
};
// sofi: http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/PhotoshopFileFormats.htm#50577409_70055
struct psd_layer_effects_color_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_color_overlay() {
setFillType(psd_fill_solid_color);
setColor(Qt::white);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setColor;
};
struct psd_layer_effects_gradient_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_gradient_overlay()
{
setFillType(psd_fill_gradient);
setAngle(90);
setReverse(false);
setScale(100);
setAlignWithLayer(true);
setStyle(psd_gradient_style_linear);
}
public:
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setGradient;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_overlay_base::setReverse;
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setStyle;
// using psd_layer_effects_overlay_base::setGradientOffset;
// using psd_layer_effects_overlay_base::gradientOffset;
};
struct psd_layer_effects_pattern_overlay : public psd_layer_effects_overlay_base
{
psd_layer_effects_pattern_overlay()
{
setFillType(psd_fill_pattern);
setScale(100);
setAlignWithLayer(true);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setPattern;
// using psd_layer_effects_overlay_base::setPatternPhase;
// using psd_layer_effects_overlay_base::patternPhase;
private:
// These are unused
/*int m_scale;
bool m_alignWithLayer;
KoPattern *m_pattern;
int m_horizontalPhase;
int m_verticalPhase;*/
};
struct psd_layer_effects_stroke : public psd_layer_effects_overlay_base
{
psd_layer_effects_stroke()
: m_position(psd_stroke_outside)
{
setFillType(psd_fill_solid_color);
setColor(Qt::black);
setAngle(90);
setReverse(false);
setScale(100);
setAlignWithLayer(true);
setStyle(psd_gradient_style_linear);
setScale(100);
setAlignWithLayer(true);
}
/// FIXME: 'using' is not supported by MSVC, so please refactor in
/// some other way to ensure that the setters are not used
/// in the classes we don't want
// using psd_layer_effects_shadow_base::setFillType;
// using psd_layer_effects_shadow_base::setSize;
// using psd_layer_effects_shadow_base::setColor;
// using psd_layer_effects_shadow_base::setGradient;
// using psd_layer_effects_shadow_base::setAngle;
// using psd_layer_effects_overlay_base::setReverse;
// using psd_layer_effects_overlay_base::setScale;
// using psd_layer_effects_overlay_base::setAlignWithLayer;
// using psd_layer_effects_overlay_base::setStyle;
// using psd_layer_effects_overlay_base::setGradientOffset;
// using psd_layer_effects_overlay_base::gradientOffset;
// using psd_layer_effects_overlay_base::setPattern;
// using psd_layer_effects_overlay_base::setPatternPhase;
// using psd_layer_effects_overlay_base::patternPhase;
psd_stroke_position position() const {
return m_position;
}
void setPosition(psd_stroke_position value) {
m_position = value;
}
private:
psd_stroke_position m_position;
};
/**
* Convert PsdColorMode to pigment colormodelid and colordepthid.
* @see KoColorModelStandardIds
*
* @return a QPair containing ColorModelId and ColorDepthID
*/
QPair<QString, QString> KRITAPSD_EXPORT psd_colormode_to_colormodelid(psd_color_mode colormode, quint16 channelDepth);
/**
* Convert the Photoshop blend mode strings to Pigment compositeop id's
*/
QString KRITAPSD_EXPORT psd_blendmode_to_composite_op(const QString& blendmode);
QString KRITAPSD_EXPORT composite_op_to_psd_blendmode(const QString& compositeOp);
#endif // PSD_H
diff --git a/krita/libpsd/psd_pattern.cpp b/krita/libpsd/psd_pattern.cpp
index 875bd583a62..ba1bccb9e2a 100644
--- a/krita/libpsd/psd_pattern.cpp
+++ b/krita/libpsd/psd_pattern.cpp
@@ -1,217 +1,217 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_pattern.h"
#include "psd_utils.h"
#include "compression.h"
#include <QImage>
-#include <QDebug>
+#include <kis_debug.h>
struct PsdPattern::Private
{
KoPattern *patternResource;
};
PsdPattern::PsdPattern()
: d(new Private())
{
d->patternResource = 0;
}
PsdPattern::~PsdPattern()
{
delete d;
}
void PsdPattern::setPattern(KoPattern *pattern)
{
d->patternResource = pattern;
}
KoPattern *PsdPattern::pattern() const
{
return d->patternResource;
}
bool psd_write_pattern(QIODevice *io)
{
Q_UNUSED(io);
return false;
}
bool psd_read_pattern(QIODevice *io)
{
quint32 pattern_length;
psd_pattern pattern;
memset(&pattern, 0, sizeof(psd_pattern));
psdread(io, &pattern_length);
pattern_length = (pattern_length + 3) & ~3;
psdread(io, &pattern.version);
if (pattern.version != 1) return false;
psdread(io, (quint32*)&pattern.color_mode);
psdread(io, &pattern.height);
psdread(io, &pattern.width);
psdread_unicodestring(io, pattern.name);
psdread_pascalstring(io, pattern.uuid, 2);
if (pattern.color_mode == Indexed) {
pattern.color_table.reserve(256);
quint8 r;
quint8 g;
quint8 b;
for (int i = 0; i < 256; ++i) {
psdread(io, &r);
psdread(io, &g);
psdread(io, &b);
pattern.color_table.append(qRgb(r, g, b));
}
}
// Now load the virtual memory array
psdread(io, &pattern.version);
if (pattern.version != 3) return false;
quint32 vm_array_length;
psdread(io, &vm_array_length);
psdread(io, &pattern.top);
psdread(io, &pattern.left);
psdread(io, &pattern.bottom);
psdread(io, &pattern.right);
QImage img;
if (pattern.color_mode == Indexed) {
img = QImage(pattern.width, pattern.height, QImage::Format_Indexed8);
img.setColorTable(pattern.color_table);
}
else {
img = QImage(pattern.width, pattern.height, QImage::Format_ARGB32);
}
qint32 max_channels;
psdread(io, &max_channels);
QVector<QByteArray> channelData;
for (int i = 0; i < max_channels; ++i) {
quint32 written;
qint32 len;
quint32 pixel_depth1;
quint32 top;
quint32 left;
quint32 bottom;
quint32 right;
quint16 pixel_depth2;
quint8 compression_mode;
psdread(io, &written);
psdread(io, &len);
len -= 4 + 4 * 4 + 2 + 1;
if (len < 0) {
continue;
}
if (written > 0) {
if (pattern.channel_number == 0) {
psdread(io, &pixel_depth1);
}
else {
quint32 d;
psdread(io, &d);
Q_ASSERT(d == pixel_depth1);
}
}
else {
quint32 d;
psdread(io, &d);
}
psdread(io, &top);
psdread(io, &left);
psdread(io, &bottom);
psdread(io, &right);
psdread(io, &pixel_depth2);
psdread(io, &compression_mode);
quint32 per_channel_length = 0;
if (written > 0) {
if (pattern.channel_number == 0) {
qint32 pixels;
qint32 length;
pixels = length = pattern.width * pattern.height;
switch(pixel_depth1) {
case 1:
length = (pattern.width + 7) / 8 * pattern.height;
break;
case 8:
break;
case 16:
length *= 2;
pixels *= 2;
default:
- qDebug() << "Wrong depth for pattern";
+ dbgKrita << "Wrong depth for pattern";
return false;
}
per_channel_length = length;
Q_UNUSED(per_channel_length); // wtf!?
switch(pattern.color_mode) {
case Bitmap:
case Indexed:
break;
case Grayscale:
case DuoTone:
length *= 2;
break;
case RGB:
length *= 4;
break;
case CMYK:
length *= 5;
break;
case Lab:
length *= 4;
break;
case MultiChannel:
length *= 4;
default:
- qDebug() << "Impossible color mode" << pattern.color_mode;
+ dbgKrita << "Impossible color mode" << pattern.color_mode;
return false;
}
QByteArray ba = io->read(len);
channelData << Compression::uncompress(length, ba, (Compression::CompressionType)compression_mode);
}
}
}
return true;
}
diff --git a/krita/libpsd/psd_utils.cpp b/krita/libpsd/psd_utils.cpp
index c5098b66753..cfc14934fde 100644
--- a/krita/libpsd/psd_utils.cpp
+++ b/krita/libpsd/psd_utils.cpp
@@ -1,282 +1,282 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_utils.h"
#include <QtEndian>
#include <QIODevice>
#include <QString>
-#include <QDebug>
+#include <kis_debug.h>
#include <netinet/in.h> // htonl
#include "psd.h"
bool psdwrite(QIODevice* io, quint8 v)
{
int written = io->write((char*)&v, 1);
return written == 1;
}
bool psdwrite(QIODevice* io, quint16 v)
{
quint16 val = htons(v);
int written = io->write((char*)&val, 2);
return written == 2;
}
bool psdwrite(QIODevice* io, qint16 v)
{
qint16 val = htons(v);
int written = io->write((char*)&val, 2);
return written == 2;
}
bool psdwrite(QIODevice* io, quint32 v)
{
quint32 val = htonl(v);
int written = io->write((char*)&val, 4);
return written == 4;
}
bool psdwrite(QIODevice* io, double val)
{
Q_ASSERT(sizeof(double) == sizeof(qint64));
void *v = &val;
qint64 i = qToBigEndian<qint64>(*(qint64*)(v));
quint64 write = io->write((char*)&i, 8);
if (write != 8) return false;
return true;
}
bool psdpad(QIODevice* io, quint32 padding)
{
char* pad = new char[padding];
memset(pad, 0, padding);
quint32 written = io->write(pad, padding);
delete [] pad;
return written == padding;
}
bool psdwrite(QIODevice* io, const QString &s)
{
int l = s.length();
QByteArray b = s.toLatin1();
char* str = b.data();
int written = io->write(str, l);
return written == l;
}
bool psdwrite_pascalstring(QIODevice* io, const QString &s)
{
Q_ASSERT(s.length() < 256);
Q_ASSERT(s.length() >= 0);
if (s.length() < 0 || s.length() > 255) return false;
if (s.isNull()) {
psdwrite(io, (quint8)0);
psdwrite(io, (quint8)0);
return true;
}
quint8 length = s.length();
psdwrite(io, length);
QByteArray b = s.toLatin1();
char* str = b.data();
int written = io->write(str, length);
if (written != length) return false;
if ((length & 0x01) != 0) {
return psdwrite(io, (quint8)0);
}
return true;
}
bool psdwrite_pascalstring(QIODevice* io, const QString &s, int padding)
{
Q_ASSERT(s.length() < 256);
Q_ASSERT(s.length() >= 0);
if (s.length() < 0 || s.length() > 255) return false;
if (s.isNull()) {
psdwrite(io, (quint8)0);
psdwrite(io, (quint8)0);
return true;
}
quint8 length = s.length();
psdwrite(io, length);
QByteArray b = s.toLatin1();
char* str = b.data();
int written = io->write(str, length);
if (written != length) return false;
// If the total length (length byte + content) is not a multiple of padding, add zeroes to pad
length++;
if ((length % padding) != 0) {
for (int i = 0; i < (padding - (length %padding)); i++) {
psdwrite(io, (quint8)0);
}
}
return true;
}
bool psdread(QIODevice *io, quint8 *v)
{
quint64 read = io->read((char*)v, 1);
if (read != 1) return false;
return true;
}
bool psdread(QIODevice* io, quint16* v)
{
quint16 val;
quint64 read = io->read((char*)&val, 2);
if (read != 2) return false;
*v = ntohs(val);
return true;
}
bool psdread(QIODevice* io, qint16* v)
{
qint16 val;
quint64 read = io->read((char*)&val, 2);
if (read != 2) return false;
*v = ntohs(val);
return true;
}
bool psdread(QIODevice* io, quint32* v)
{
quint32 val;
quint64 read = io->read((char*)&val, 4);
if (read != 4) return false;
*v = ntohl(val);
return true;
}
bool psdread(QIODevice* io, qint32* v)
{
qint32 val;
quint64 read = io->read((char*)&val, 4);
if (read != 4) return false;
*v = ntohl(val);
return true;
}
bool psdread(QIODevice* io, quint64* v)
{
quint64 val;
quint64 read = io->read((char*)&val, 8);
if (read != 8) return false;
*v = qFromBigEndian<qint64>((quint8*)&val);
return true;
}
bool psdread(QIODevice* io, double* v)
{
Q_ASSERT(sizeof(double) == sizeof(qint64));
qint64 val;
quint64 read = io->read((char*)&val, 8);
if (read != 8) return false;
*(reinterpret_cast<qint64*>(v)) = qFromBigEndian<qint64>(val);
return true;
}
bool psdread_pascalstring(QIODevice* io, QString& s, int padding)
{
quint8 length;
if (!psdread(io, &length)) {
return false;
}
if (length == 0) {
// read the padding
for (int i = 0; i < padding -1; ++i) {
io->seek(io->pos() + 1);
}
return (length == 0);
}
QByteArray chars = io->read(length);
if (chars.length() != length) {
return false;
}
// read padding byte
quint32 paddedLength = length + 1;
if (padding > 0) {
while (paddedLength % padding != 0) {
if (!io->seek(io->pos() + 1)) {
return false;
}
paddedLength++;
}
}
s.append(QString::fromLatin1(chars));
return true;
}
bool psd_read_blendmode(QIODevice *io, QString &blendModeKey)
{
QByteArray b;
b = io->read(4);
if(b.size() != 4 || QString(b) != "8BIM") {
return false;
}
blendModeKey = QString(io->read(4));
if (blendModeKey.size() != 4) {
return false;
}
return true;
}
bool psdread_unicodestring(QIODevice *io, QString &s)
{
quint32 stringlen;
if (!psdread(io, &stringlen)) {
return false;
}
for (uint i = 0; i < stringlen; ++i) {
quint16 ch;
if (!psdread(io, &ch)) {
return false;
}
if (ch != 0) {
QChar uch(ch);
s.append(uch);
}
}
return true;
}
diff --git a/krita/libpsd/tests/kis_asl_parser_test.cpp b/krita/libpsd/tests/kis_asl_parser_test.cpp
index 1982acfa359..54a5e3835ea 100644
--- a/krita/libpsd/tests/kis_asl_parser_test.cpp
+++ b/krita/libpsd/tests/kis_asl_parser_test.cpp
@@ -1,331 +1,332 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_parser_test.h"
#include <qtest_kde.h>
#include "testutil.h"
#include <boost/bind.hpp>
#include <QDomDocument>
#include "KoPattern.h"
#include <asl/kis_asl_reader.h>
#include <asl/kis_asl_xml_parser.h>
#include <asl/kis_asl_object_catcher.h>
#include <asl/kis_asl_callback_object_catcher.h>
+#include <kis_debug.h>
void KisAslParserTest::test()
{
QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QFile aslFile(fileName);
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
- qDebug() << ppVar(doc.toString());
+ dbgKrita << ppVar(doc.toString());
KisAslObjectCatcher trivialCatcher;
KisAslXmlParser parser;
parser.parseXML(doc, trivialCatcher);
}
struct CallbackVerifier {
CallbackVerifier() : m_numCallsHappened(0) {}
void setColor(const QColor &color) {
QVERIFY(color == QColor(Qt::white));
m_numCallsHappened++;
}
void setOpacity(double opacity) {
QVERIFY(qFuzzyCompare(opacity, 75));
m_numCallsHappened++;
}
void setBlendingMode(const QString &mode) {
QVERIFY(mode == "Scrn");
m_numCallsHappened++;
}
void setEnabled(bool value) {
QVERIFY(value);
m_numCallsHappened++;
}
void setCurve(const QString &name, const QVector<QPointF> &points) {
QCOMPARE(name, QString("Linear"));
QCOMPARE(points[0], QPointF());
QCOMPARE(points[1], QPointF(255.0, 255.0));
m_numCallsHappened++;
}
void setText(const QString &text) {
QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4"));
m_numCallsHappened++;
}
void setPattern(const KoPattern *pattern) {
- qDebug() << ppVar(pattern->name());
- qDebug() << ppVar(pattern->filename());
+ dbgKrita << ppVar(pattern->name());
+ dbgKrita << ppVar(pattern->filename());
//QCOMPARE(text, QString("11adf7a2-a120-11e1-957c-d1ee226781a4"));
m_numCallsHappened++;
}
int m_numCallsHappened;
};
void KisAslParserTest::testWithCallbacks()
{
QString fileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QFile aslFile(fileName);
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
KisAslCallbackObjectCatcher c;
CallbackVerifier verifier;
c.subscribeColor("/Styl/Lefx/IrGl/Clr ", boost::bind(&CallbackVerifier::setColor, &verifier, _1));
c.subscribeUnitFloat("/Styl/Lefx/IrGl/Opct", "#Prc", boost::bind(&CallbackVerifier::setOpacity, &verifier, _1));
c.subscribeEnum("/Styl/Lefx/IrGl/Md ", "BlnM", boost::bind(&CallbackVerifier::setBlendingMode, &verifier, _1));
c.subscribeBoolean("/Styl/Lefx/IrGl/enab", boost::bind(&CallbackVerifier::setEnabled, &verifier, _1));
c.subscribeCurve("/Styl/Lefx/OrGl/TrnS", boost::bind(&CallbackVerifier::setCurve, &verifier, _1, _2));
c.subscribeText("/null/Idnt", boost::bind(&CallbackVerifier::setText, &verifier, _1));
KisAslXmlParser parser;
parser.parseXML(doc, c);
QCOMPARE(verifier.m_numCallsHappened, 6);
}
#include <asl/kis_asl_xml_writer.h>
void KisAslParserTest::testASLXMLWriter()
{
KisAslXmlWriter w;
QImage testImage(QSize(16, 16), QImage::Format_ARGB32);
KoPattern testPattern1(testImage, "Some very nice name ;)", "");
KoPattern testPattern2(testImage, "Another very nice name ;P", "");
w.enterList("Patterns");
w.writePattern("", &testPattern1);
w.writePattern("", &testPattern2);
w.leaveList();
w.enterDescriptor("", "", "null");
w.writeText("Nm ", "www.designpanoply.com - Freebie 5");
w.writeText("Idnt", "11adf7a2-a120-11e1-957c-d1ee226781a4");
w.leaveDescriptor();
w.enterDescriptor("", "", "Styl");
w.enterDescriptor("documentMode", "", "documentMode");
w.leaveDescriptor();
w.enterDescriptor("Lefx", "", "Lefx");
w.writeUnitFloat("Scl ", "#Prc", 100);
w.writeBoolean("masterFxSwitch", true);
w.enterDescriptor("DrSh", "", "DrSh");
w.writeBoolean("enab", true);
w.writeEnum("Md ", "BlnM", "Mltp");
w.writeColor("Clr ", Qt::green);
w.writeUnitFloat("Opct", "#Prc", 16);
w.writeBoolean("uglg", false);
w.writeUnitFloat("lagl", "#Prc", 100);
w.writeUnitFloat("Dstn", "#Pxl", 100);
w.writeUnitFloat("Ckmt", "#Pxl", 100);
w.writeUnitFloat("blur", "#Pxl", 100);
w.writeUnitFloat("Nose", "#Prc", 100);
w.writeBoolean("anta", true);
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeBoolean("layerConceals", true);
w.leaveDescriptor();
w.leaveDescriptor();
w.leaveDescriptor();
- qDebug() << ppVar(w.document().toString());
+ dbgKrita << ppVar(w.document().toString());
}
#include <KoStopGradient.h>
#include <KoSegmentGradient.h>
void KisAslParserTest::testWritingGradients()
{
KisAslXmlWriter w1;
KoSegmentGradient segmentGradient("");
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.0, 0.3, 0.15,
Qt::black, Qt::red);
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.3, 0.6, 0.45,
Qt::red, Qt::green);
segmentGradient.createSegment(INTERP_LINEAR, COLOR_INTERP_RGB,
0.6, 1.0, 0.8,
Qt::green, Qt::white);
w1.writeSegmentGradient("tstG", &segmentGradient);
- //qDebug() << "===";
- //qDebug() << ppVar(w1.document().toString());
+ //dbgKrita << "===";
+ //dbgKrita << ppVar(w1.document().toString());
KisAslXmlWriter w2;
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoGradientStop> stops;
stops << KoGradientStop(0.0, KoColor(Qt::black, cs));
stops << KoGradientStop(0.3, KoColor(Qt::red, cs));
stops << KoGradientStop(0.6, KoColor(Qt::green, cs));
stops << KoGradientStop(1.0, KoColor(Qt::white, cs));
KoStopGradient stopGradient("");
stopGradient.setStops(stops);
w2.writeStopGradient("tstG", &stopGradient);
- //qDebug() << "===";
- //qDebug() << ppVar(w2.document().toString());
+ //dbgKrita << "===";
+ //dbgKrita << ppVar(w2.document().toString());
QCOMPARE(w1.document().toString(),
w2.document().toString());
}
#include <asl/kis_asl_writer.h>
void KisAslParserTest::testASLWriter()
{
//QString srcFileName(TestUtil::fetchDataFileLazy("asl/testset/freebie_with_pattern.asl"));
QString srcFileName(TestUtil::fetchDataFileLazy("asl/freebie.asl"));
QDomDocument srcDoc;
{
QFile srcAslFile(srcFileName);
srcAslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
srcDoc = reader.readFile(&srcAslFile);
QFile tfile("src_parsed.xml");
tfile.open(QIODevice::WriteOnly);
tfile.write(srcDoc.toByteArray());
tfile.close();
}
QString dstFileName("test.asl");
{
QFile dstAslFile(dstFileName);
dstAslFile.open(QIODevice::WriteOnly);
KisAslWriter writer;
writer.writeFile(&dstAslFile, srcDoc);
dstAslFile.flush();
dstAslFile.close();
}
QDomDocument dstDoc;
{
QFile roundTripAslFile(dstFileName);
roundTripAslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
dstDoc = reader.readFile(&roundTripAslFile);
QFile tfile("dst_parsed.xml");
tfile.open(QIODevice::WriteOnly);
tfile.write(dstDoc.toByteArray());
tfile.close();
}
QCOMPARE(srcDoc.toByteArray(), dstDoc.toByteArray());
}
void KisAslParserTest::testParserWithPatterns()
{
QDir dir(QString(FILES_DATA_DIR) + QDir::separator() + "testset");
QFileInfoList files = dir.entryInfoList(QStringList() << "*.asl", QDir::Files);
int index = 0;
foreach (const QFileInfo &fileInfo, files) {
//if (index != 12) {index++; continue;}
- qDebug() << "===" << index << "===";
- qDebug() << ppVar(fileInfo.fileName());
+ dbgKrita << "===" << index << "===";
+ dbgKrita << ppVar(fileInfo.fileName());
QFile aslFile(fileInfo.absoluteFilePath());
aslFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
QFile xmlFile("mydata.xml");
xmlFile.open(QIODevice::WriteOnly);
xmlFile.write(doc.toByteArray());
- //qDebug() << ppVar(doc.toString());
+ //dbgKrita << ppVar(doc.toString());
CallbackVerifier verifier;
KisAslCallbackObjectCatcher c;
c.subscribePattern("/Patterns/KisPattern", boost::bind(&CallbackVerifier::setPattern, &verifier, _1));
KisAslXmlParser parser;
parser.parseXML(doc, c);
//QCOMPARE(verifier.m_numCallsHappened, 7);
index++;
//break;
}
}
QTEST_KDEMAIN(KisAslParserTest, GUI)
diff --git a/krita/main.cc b/krita/main.cc
index 15074c9ef8e..df34d2a84bb 100644
--- a/krita/main.cc
+++ b/krita/main.cc
@@ -1,199 +1,169 @@
/*
- * main.cc - part of KImageShop
- *
- * Copyright (c) 1999 Matthias Elter <me@kde.org>
- * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
+ * Copyright (c) 1999 Matthias Elter <me@kde.org>
+ * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
+ * Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <QString>
#include <QPixmap>
-#include <QDebug>
+#include <kis_debug.h>
#include <QProcess>
#include <QProcessEnvironment>
#include <QDesktopServices>
#include <QDir>
#include <QDate>
-#include <QDebug>
+#include <QLoggingCategory>
-#include <kcmdlineargs.h>
+#include <kis_debug.h>
#include <KisApplication.h>
#include <KoConfig.h>
#include "data/splash/splash_screen.xpm"
#include "data/splash/splash_holidays.xpm"
#include "ui/kis_aboutdata.h"
#include "ui/kis_factory2.h"
#include "ui/KisDocument.h"
#include "kis_splash_screen.h"
#include "KisPart.h"
#include "opengl/kis_opengl.h"
+#include "KisApplicationArguments.h"
#if defined Q_OS_WIN
#include <Windows.h>
#include <stdlib.h>
#include <ui/input/wintab/kis_tablet_support_win.h>
#ifdef USE_BREAKPAD
#include "kis_crash_handler.h"
#endif
#elif defined HAVE_X11
#include <ui/input/wintab/kis_tablet_support_x11.h>
#endif
extern "C" int main(int argc, char **argv)
{
bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty();
#ifdef HAVE_X11
if (runningInKDE) {
qputenv("QT_NO_GLIB", "1");
}
#endif
#ifdef USE_BREAKPAD
qputenv("KDE_DEBUG", "1");
KisCrashHandler crashHandler;
Q_UNUSED(crashHandler);
#endif
#if defined Q_OS_WIN
SetProcessDPIAware(); // The n-trig wintab driver needs this to report the correct dimensions
#endif
- int state;
- K4AboutData *aboutData = KisFactory::aboutData();
-
- KCmdLineArgs::init(argc, argv, aboutData);
-
- KCmdLineOptions options;
- options.add("print", ki18n("Only print and exit"));
- options.add("template", ki18n("Open a new document with a template"));
- options.add("dpi <dpiX,dpiY>", ki18n("Override display DPI"));
- options.add("export-pdf", ki18n("Only export to PDF and exit"));
- options.add("export", ki18n("Export to the given filename and exit"));
- options.add("export-filename <filename>", ki18n("Filename for export/export-pdf"));
- options.add("profile-filename <filename>", ki18n("Filename to write profiling information into."));
- options.add("+[file(s)]", ki18n("File(s) or URL(s) to open"));
-
- KCmdLineArgs::addCmdLineOptions(options);
+ // Disable all debug output by default
+ // You can re-enable debug output by starting Krita like "QT_LOGGING_RULES="krita*=true" krita"
+ // See: http://doc.qt.io/qt-5/qloggingcategory.html
+ QLoggingCategory::setFilterRules("*=false");
// A per-user unique string, without /, because QLocalServer cannot use names with a / in it
QString key = "Krita" +
QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_");
key = key.replace(":", "_").replace("\\","_");
#if defined HAVE_X11
// we need to call XInitThreads() (which this does) because of gmic (and possibly others)
// do their own X11 stuff in their own threads
// this call must happen before the creation of the application (see AA_X11InitThreads docs)
QCoreApplication::setAttribute(Qt::AA_X11InitThreads, true);
#endif
#if defined HAVE_OPENGL
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
#endif
// first create the application so we can create a pixmap
- KisApplication app(key);
-
+ KisApplication app(key, argc, argv);
// If we should clear the config, it has to be done as soon as possible after
// KisApplication has been created. Otherwise the config file may have been read
// and stored in a KConfig object we have no control over.
app.askClearConfig();
+ KisApplicationArguments args(app);
+
// create factory only after application, the componentData it creates in the
// constructor will need an existing QCoreApplication at least with Qt5/KF5,
// to set name of application etc., as also needed to find resources
KisFactory factory;
Q_UNUSED(factory); // Not really, it'll self-destruct on exiting main
if (app.isRunning()) {
-
- KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
-
// only pass arguments to main instance if they are not for batch processing
// any batch processing would be done in this separate instance
- const bool batchRun =
- args->isSet("print") || args->isSet("export") || args->isSet("export-pdf");
+ const bool batchRun = (args.print() || args.exportAs() || args.exportAsPdf());
if (!batchRun) {
-
- QByteArray ba;
- QDataStream ds(&ba, QIODevice::WriteOnly);
- args->saveAppArgs(ds);
- ds.device()->close();
-
+ QByteArray ba = args.serialize();
if (app.sendMessage(ba)) {
return 0;
}
}
}
-#if defined Q_OS_WIN
- KisTabletSupportWin tabletSupportWin;
- tabletSupportWin.init();
- app.installNativeEventFilter(&tabletSupportWin);
-#elif defined HAVE_X11
- KisTabletSupportX11 tabletSupportX11;
- tabletSupportX11.init();
- app.installNativeEventFilter(&tabletSupportX11);
-#endif
-
-
#if defined HAVE_OPENGL
KisOpenGL::initialize();
#endif
if (!runningInKDE) {
// Icons in menus are ugly and distracting
app.setAttribute(Qt::AA_DontShowIconsInMenus);
}
// then create the pixmap from an xpm: we cannot get the
// location of our datadir before we've started our components,
// so use an xpm.
QDate currentDate = QDate::currentDate();
QWidget *splash = 0;
if (currentDate > QDate(currentDate.year(), 12, 4) ||
currentDate < QDate(currentDate.year(), 1, 9)) {
- splash = new KisSplashScreen(aboutData->version(), QPixmap(splash_holidays_xpm));
+ splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm));
}
else {
- splash = new KisSplashScreen(aboutData->version(), QPixmap(splash_screen_xpm));
+ splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm));
}
app.setSplashScreen(splash);
- if (!app.start()) {
+ if (!app.start(args)) {
return 1;
}
// Set up remote arguments.
QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)),
&app, SLOT(remoteArguments(QByteArray,QObject*)));
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)),
&app, SLOT(fileOpenRequested(QString)));
- state = app.exec();
+ int state = app.exec();
return state;
}
diff --git a/krita/plugins/assistants/RulerAssistant/InfiniteRulerAssistant.cc b/krita/plugins/assistants/RulerAssistant/InfiniteRulerAssistant.cc
index c0a83f4707a..8cda39ffc13 100644
--- a/krita/plugins/assistants/RulerAssistant/InfiniteRulerAssistant.cc
+++ b/krita/plugins/assistants/RulerAssistant/InfiniteRulerAssistant.cc
@@ -1,152 +1,152 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Geoffry Song <goffrie@gmail.com>
* Copyright (c) 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "InfiniteRulerAssistant.h"
#include "kis_debug.h"
#include <klocale.h>
#include <QPainter>
#include <QLinearGradient>
#include <QTransform>
#include "kis_coordinates_converter.h"
#include "kis_algebra_2d.h"
#include <math.h>
InfiniteRulerAssistant::InfiniteRulerAssistant()
: KisPaintingAssistant("infinite ruler", i18n("Infinite Ruler assistant"))
{
}
QPointF InfiniteRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin)
{
Q_ASSERT(handles().size() == 2);
//code nicked from the perspective ruler.
qreal
dx = pt.x() - strokeBegin.x(),
dy = pt.y() - strokeBegin.y();
if (dx * dx + dy * dy < 4.0) {
// allow some movement before snapping
return strokeBegin;
}
- //qDebug()<<strokeBegin<< ", " <<*handles()[0];
+ //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
QLineF snapLine = QLineF(*handles()[0], *handles()[1]);
dx = snapLine.dx();
dy = snapLine.dy();
const qreal
dx2 = dx * dx,
dy2 = dy * dy,
invsqrlen = 1.0 / (dx2 + dy2);
QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
r *= invsqrlen;
return r;
//return pt;
}
QPointF InfiniteRulerAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin)
{
return project(pt, strokeBegin);
}
void InfiniteRulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
{
gc.save();
gc.resetTransform();
QPointF delta(0,0);//this is the difference between the window and the widget//
QPointF mousePos(0,0);
QPointF endPoint(0,0);//this is the final point that the line is being extended to, we seek it just outside the view port//
if (canvas){
//simplest, cheapest way to get the mouse-position//
mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos());
}
else {
//...of course, you need to have access to a canvas-widget for that.//
mousePos = QCursor::pos();//this'll give an offset//
dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<<canvas;
}
if (handles().size() > 1 && outline()==true && previewVisible==true) {
//don't draw if invalid.
QTransform initialTransform = converter->documentToWidgetTransform();
QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1]));
QRect viewport= gc.viewport();
KisAlgebra2D::intersectLineRect(snapLine, viewport);
QPainterPath path;
path.moveTo(snapLine.p1());
path.lineTo(snapLine.p2());
drawPreview(gc, path);//and we draw the preview.
}
gc.restore();
KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
}
void InfiniteRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
{
if (assistantVisible==false){return;}
if (handles().size() < 2) return;
QTransform initialTransform = converter->documentToWidgetTransform();
// Draw the line
QPointF p1 = *handles()[0];
QPointF p2 = *handles()[1];
gc.setTransform(initialTransform);
QPainterPath path;
path.moveTo(p1);
path.lineTo(p2);
drawPath(gc, path, snapping());
}
QPointF InfiniteRulerAssistant::buttonPosition() const
{
return (*handles()[0]);
}
InfiniteRulerAssistantFactory::InfiniteRulerAssistantFactory()
{
}
InfiniteRulerAssistantFactory::~InfiniteRulerAssistantFactory()
{
}
QString InfiniteRulerAssistantFactory::id() const
{
return "infinite ruler";
}
QString InfiniteRulerAssistantFactory::name() const
{
return i18n("Infinite Ruler");
}
KisPaintingAssistant* InfiniteRulerAssistantFactory::createPaintingAssistant() const
{
return new InfiniteRulerAssistant;
}
diff --git a/krita/plugins/assistants/RulerAssistant/ParallelRulerAssistant.cc b/krita/plugins/assistants/RulerAssistant/ParallelRulerAssistant.cc
index d21cc270885..a3716f4184b 100644
--- a/krita/plugins/assistants/RulerAssistant/ParallelRulerAssistant.cc
+++ b/krita/plugins/assistants/RulerAssistant/ParallelRulerAssistant.cc
@@ -1,160 +1,160 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Geoffry Song <goffrie@gmail.com>
* Copyright (c) 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ParallelRulerAssistant.h"
#include "kis_debug.h"
#include <klocale.h>
#include <QPainter>
#include <QLinearGradient>
#include <QTransform>
#include "kis_coordinates_converter.h"
#include "kis_algebra_2d.h"
#include <math.h>
ParallelRulerAssistant::ParallelRulerAssistant()
: KisPaintingAssistant("parallel ruler", i18n("Parallel Ruler assistant"))
{
}
QPointF ParallelRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin)
{
Q_ASSERT(handles().size() == 2);
//code nicked from the perspective ruler.
qreal
dx = pt.x() - strokeBegin.x(),
dy = pt.y() - strokeBegin.y();
if (dx * dx + dy * dy < 4.0) {
// allow some movement before snapping
return strokeBegin;
}
- //qDebug()<<strokeBegin<< ", " <<*handles()[0];
+ //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
QLineF snapLine = QLineF(*handles()[0], *handles()[1]);
QPointF translation = (*handles()[0]-strokeBegin)*-1.0;
snapLine= snapLine.translated(translation);
dx = snapLine.dx();
dy = snapLine.dy();
const qreal
dx2 = dx * dx,
dy2 = dy * dy,
invsqrlen = 1.0 / (dx2 + dy2);
QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
r *= invsqrlen;
return r;
//return pt;
}
QPointF ParallelRulerAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin)
{
return project(pt, strokeBegin);
}
void ParallelRulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
{
gc.save();
gc.resetTransform();
QPointF delta(0,0);//this is the difference between the vanishing point and the mouse-position//
QPointF mousePos(0,0);
QPointF endPoint(0,0);//this is the final point that the line is being extended to, we seek it just outside the view port//
if (canvas){
//simplest, cheapest way to get the mouse-position//
mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos());
}
else {
//...of course, you need to have access to a canvas-widget for that.//
mousePos = QCursor::pos();//this'll give an offset//
dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<<canvas;
}
if (handles().size() > 1 && outline()==true && previewVisible==true) {
//don't draw if invalid.
QTransform initialTransform = converter->documentToWidgetTransform();
QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1]));
QPointF translation = (initialTransform.map(*handles()[0])-mousePos)*-1.0;
snapLine= snapLine.translated(translation);
QRect viewport= gc.viewport();
KisAlgebra2D::intersectLineRect(snapLine, viewport);
QPainterPath path;
path.moveTo(snapLine.p1());
path.lineTo(snapLine.p2());
drawPreview(gc, path);//and we draw the preview.
}
gc.restore();
KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
}
void ParallelRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
{
if (assistantVisible==false){return;}
if (handles().size() < 2) return;
QTransform initialTransform = converter->documentToWidgetTransform();
// Draw the line
QPointF p1 = *handles()[0];
QPointF p2 = *handles()[1];
gc.setTransform(initialTransform);
QPainterPath path;
path.moveTo(p1);
path.lineTo(p2);
drawPath(gc, path, snapping());
}
QPointF ParallelRulerAssistant::buttonPosition() const
{
return (*handles()[0] + *handles()[1]) * 0.5;
}
ParallelRulerAssistantFactory::ParallelRulerAssistantFactory()
{
}
ParallelRulerAssistantFactory::~ParallelRulerAssistantFactory()
{
}
QString ParallelRulerAssistantFactory::id() const
{
return "parallel ruler";
}
QString ParallelRulerAssistantFactory::name() const
{
return i18n("Parallel Ruler");
}
KisPaintingAssistant* ParallelRulerAssistantFactory::createPaintingAssistant() const
{
return new ParallelRulerAssistant;
}
diff --git a/krita/plugins/assistants/RulerAssistant/VanishingPointAssistant.cc b/krita/plugins/assistants/RulerAssistant/VanishingPointAssistant.cc
index 15619663953..59641178bf2 100644
--- a/krita/plugins/assistants/RulerAssistant/VanishingPointAssistant.cc
+++ b/krita/plugins/assistants/RulerAssistant/VanishingPointAssistant.cc
@@ -1,161 +1,161 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Geoffry Song <goffrie@gmail.com>
* Copyright (c) 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "VanishingPointAssistant.h"
#include "kis_debug.h"
#include <klocale.h>
#include <QPainter>
#include <QLinearGradient>
#include <QTransform>
#include "kis_coordinates_converter.h"
#include "kis_algebra_2d.h"
#include <math.h>
VanishingPointAssistant::VanishingPointAssistant()
: KisPaintingAssistant("vanishing point", i18n("Vanishing Point assistant"))
{
}
QPointF VanishingPointAssistant::project(const QPointF& pt, const QPointF& strokeBegin)
{
//Q_ASSERT(handles().size() == 1 || handles().size() == 5);
//code nicked from the perspective ruler.
qreal
dx = pt.x() - strokeBegin.x(),
dy = pt.y() - strokeBegin.y();
if (dx * dx + dy * dy < 4.0) {
// allow some movement before snapping
return strokeBegin;
}
- //qDebug()<<strokeBegin<< ", " <<*handles()[0];
+ //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
QLineF snapLine = QLineF(*handles()[0], strokeBegin);
dx = snapLine.dx();
dy = snapLine.dy();
const qreal
dx2 = dx * dx,
dy2 = dy * dy,
invsqrlen = 1.0 / (dx2 + dy2);
QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
r *= invsqrlen;
return r;
//return pt;
}
QPointF VanishingPointAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin)
{
return project(pt, strokeBegin);
}
void VanishingPointAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
{
gc.save();
gc.resetTransform();
QPointF delta(0,0);
QPointF mousePos(0,0);
QPointF endPoint(0,0);//this is the final point that the line is being extended to, we seek it just outside the view port//
if (canvas){
//simplest, cheapest way to get the mouse-position//
mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos());
}
else {
//...of course, you need to have access to a canvas-widget for that.//
mousePos = QCursor::pos();//this'll give an offset//
dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<<canvas;
}
if (handles().size() > 0 && outline()==true && previewVisible==true) {
//don't draw if invalid.
QTransform initialTransform = converter->documentToWidgetTransform();
QPointF startPoint = initialTransform.map(*handles()[0]);
QLineF snapLine= QLineF(startPoint, mousePos);
QRect viewport= gc.viewport();
KisAlgebra2D::intersectLineRect(snapLine, viewport);
QRect bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint());
QPainterPath path;
if (bounds.contains(startPoint.toPoint())){
path.moveTo(startPoint);
path.lineTo(snapLine.p1());
}
else
{path.moveTo(snapLine.p1());
path.lineTo(snapLine.p2());}
drawPreview(gc, path);//and we draw the preview.
}
gc.restore();
KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
}
void VanishingPointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
{
if (assistantVisible==false){return;}
if (handles().size() < 1) return;
QTransform initialTransform = converter->documentToWidgetTransform();
gc.setTransform(initialTransform);
QPointF p0 = *handles()[0];
QPainterPath path;
path.moveTo(QPointF(p0.x() - 10.0, p0.y() - 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() + 10.0));
path.moveTo(QPointF(p0.x() - 10.0, p0.y() + 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() - 10.0));
drawPath(gc, path, snapping());
}
QPointF VanishingPointAssistant::buttonPosition() const
{
return (*handles()[0]);
}
VanishingPointAssistantFactory::VanishingPointAssistantFactory()
{
}
VanishingPointAssistantFactory::~VanishingPointAssistantFactory()
{
}
QString VanishingPointAssistantFactory::id() const
{
return "vanishing point";
}
QString VanishingPointAssistantFactory::name() const
{
return i18n("Vanishing Point");
}
KisPaintingAssistant* VanishingPointAssistantFactory::createPaintingAssistant() const
{
return new VanishingPointAssistant;
}
diff --git a/krita/plugins/colorspaces/extensions/CMakeLists.txt b/krita/plugins/colorspaces/extensions/CMakeLists.txt
index abd72934dcf..e1d284aa9bf 100644
--- a/krita/plugins/colorspaces/extensions/CMakeLists.txt
+++ b/krita/plugins/colorspaces/extensions/CMakeLists.txt
@@ -1,23 +1,23 @@
set( extensions_plugin_PART_SRCS
extensions_plugin.cc
kis_hsv_adjustment.cpp
kis_dodgehighlights_adjustment.cpp
kis_dodgemidtones_adjustment.cpp
kis_dodgeshadows_adjustment.cpp
kis_burnhighlights_adjustment.cpp
kis_burnmidtones_adjustment.cpp
kis_burnshadows_adjustment.cpp
kis_color_balance_adjustment.cpp
kis_desaturate_adjustment.cpp
)
add_library(krita_colorspaces_extensions MODULE ${extensions_plugin_PART_SRCS} )
kcoreaddons_desktop_to_json(krita_colorspaces_extensions krita_colorspaces_extensions_plugin.desktop)
-target_link_libraries(krita_colorspaces_extensions pigmentcms ${OPENEXR_LIBRARIES} KF5::KDELibs4Support)
+target_link_libraries(krita_colorspaces_extensions pigmentcms kritaglobal ${OPENEXR_LIBRARIES} KF5::KDELibs4Support)
install( TARGETS krita_colorspaces_extensions DESTINATION ${CALLIGRA_PLUGIN_INSTALL_DIR} )
########### install files ###############
install( FILES krita_colorspaces_extensions_plugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}/calligra )
diff --git a/krita/plugins/colorspaces/extensions/kis_burnhighlights_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_burnhighlights_adjustment.cpp
index 5ae3dfe6af3..f1ecb26c18a 100644
--- a/krita/plugins/colorspaces/extensions/kis_burnhighlights_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_burnhighlights_adjustment.cpp
@@ -1,139 +1,139 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_burnhighlights_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits>
class KisBurnHighlightsAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisBurnHighlightsAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue;
const float factor(1.0 - exposure * (0.33333));
while(nPixels > 0) {
value_red = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red);
value_green = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green);
value_blue = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue);
dst->red = KoColorSpaceMaths< float, _channel_type_>::scaleToA(value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_>::scaleToA(value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisBurnHighlightsAdjustmentFactory::KisBurnHighlightsAdjustmentFactory()
: KoColorTransformationFactory("BurnHighlights")
{
}
QList< QPair< KoID, KoID > > KisBurnHighlightsAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisBurnHighlightsAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnHighlightsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnHighlightsAdjustment::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisBurnHighlightsAdjustment< quint8, KoBgrTraits < quint8 > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisBurnHighlightsAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisBurnHighlightsAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisBurnHighlightsAdjustment< float, KoRgbTraits < float > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnHighlightsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnHighlightsAdjustment::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_burnmidtones_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_burnmidtones_adjustment.cpp
index 8e7b743c319..5db72966cd2 100644
--- a/krita/plugins/colorspaces/extensions/kis_burnmidtones_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_burnmidtones_adjustment.cpp
@@ -1,139 +1,139 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_burnmidtones_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits>
class KisBurnMidtonesAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisBurnMidtonesAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue;
const float factor(1.0 + exposure * (0.333333));
while(nPixels > 0) {
value_red = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red), factor);
value_green = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green), factor);
value_blue = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue), factor);
dst->red = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisBurnMidtonesAdjustmentFactory::KisBurnMidtonesAdjustmentFactory()
: KoColorTransformationFactory("BurnMidtones")
{
}
QList< QPair< KoID, KoID > > KisBurnMidtonesAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisBurnMidtonesAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnMidtonesAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnMidtonesAdjustment::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisBurnMidtonesAdjustment< float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisBurnMidtonesAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisBurnMidtonesAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisBurnMidtonesAdjustment< quint8, KoBgrTraits < quint8 > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnMidtonesAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnMidtonesAdjustment::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_burnshadows_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_burnshadows_adjustment.cpp
index 3ca0c5dee3d..a9f8863ce67 100644
--- a/krita/plugins/colorspaces/extensions/kis_burnshadows_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_burnshadows_adjustment.cpp
@@ -1,146 +1,146 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_burnshadows_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits>
class KisBurnShadowsAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisBurnShadowsAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue, new_value_red, new_value_green, new_value_blue;
const float factor(exposure * 0.333333);
while (nPixels > 0) {
value_red = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red);
value_green = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green);
value_blue = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue);
if( value_red < factor ) new_value_red = 0;
else new_value_red = (value_red - factor)/(1 - factor);
if( value_green < factor ) new_value_green = 0;
else new_value_green = (value_green - factor)/(1 - factor);
if( value_blue < factor ) new_value_blue = 0;
else new_value_blue = (value_blue - factor)/(1 - factor);
dst->red = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisBurnShadowsAdjustmentFactory::KisBurnShadowsAdjustmentFactory()
: KoColorTransformationFactory("BurnShadows")
{
}
QList< QPair< KoID, KoID > > KisBurnShadowsAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisBurnShadowsAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnShadowsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnShadowsAdjustment::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisBurnShadowsAdjustment< quint8, KoBgrTraits < quint8 > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisBurnShadowsAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisBurnShadowsAdjustment< quint16, KoBgrTraits < quint8 > >();
} else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisBurnShadowsAdjustment< float, KoRgbTraits < float > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisBurnShadowsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisBurnShadowsAdjustment::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_color_balance_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_color_balance_adjustment.cpp
index 0af43d10bdc..29fe0f8ea42 100644
--- a/krita/plugins/colorspaces/extensions/kis_color_balance_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_color_balance_adjustment.cpp
@@ -1,228 +1,228 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_color_balance_adjustment.h"
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <kis_debug.h>
#include <klocale.h>
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
#include <kis_hsv_adjustment.h>
#define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v )
#define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v )
class KisColorBalanceMath;
template<typename _channel_type_, typename traits>
class KisColorBalanceAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisColorBalanceAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
KisColorBalanceMath bal;
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue, hue, saturation, lightness;
while(nPixels > 0) {
float red = SCALE_TO_FLOAT(src->red);
float green = SCALE_TO_FLOAT(src->green);
float blue = SCALE_TO_FLOAT(src->blue);
RGBToHSL(red, green, blue, &hue, &saturation, &lightness);
value_red = bal.colorBalanceTransform(red, lightness, m_cyan_shadows, m_cyan_midtones, m_cyan_highlights);
value_green = bal.colorBalanceTransform(green, lightness, m_magenta_shadows, m_magenta_midtones, m_magenta_highlights);
value_blue = bal.colorBalanceTransform(blue, lightness, m_yellow_shadows, m_yellow_midtones, m_yellow_highlights);
if(m_preserve_luminosity)
{
float h1, s1, l1, h2, s2, l2;
RGBToHSL(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h1, &s1, &l1);
RGBToHSL(value_red, value_green, value_blue, &h2, &s2, &l2);
HSLToRGB(h2, s2, l1, &value_red, &value_green, &value_blue);
}
dst->red = SCALE_FROM_FLOAT(value_red);
dst->green = SCALE_FROM_FLOAT(value_green);
dst->blue = SCALE_FROM_FLOAT(value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "cyan_red_midtones" << "magenta_green_midtones" << "yellow_blue_midtones"
<< "cyan_red_shadows" << "magenta_green_shadows" << "yellow_blue_shadows"
<< "cyan_red_highlights" << "magenta_green_highlights" << "yellow_blue_highlights" << "preserve_luminosity";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "cyan_red_midtones")
return 0;
else if(name == "magenta_green_midtones")
return 1;
else if(name == "yellow_blue_midtones")
return 2;
else if (name == "cyan_red_shadows")
return 3;
else if(name == "magenta_green_shadows")
return 4;
else if(name == "yellow_blue_shadows")
return 5;
else if (name == "cyan_red_highlights")
return 6;
else if(name == "magenta_green_highlights")
return 7;
else if(name == "yellow_blue_highlights")
return 8;
else if(name == "preserve_luminosity")
return 9;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
m_cyan_midtones = parameter.toDouble();
break;
case 1:
m_magenta_midtones = parameter.toDouble();
break;
case 2:
m_yellow_midtones = parameter.toDouble();
break;
case 3:
m_cyan_shadows = parameter.toDouble();
break;
case 4:
m_magenta_shadows = parameter.toDouble();
break;
case 5:
m_yellow_shadows = parameter.toDouble();
break;
case 6:
m_cyan_highlights = parameter.toDouble();
break;
case 7:
m_magenta_highlights = parameter.toDouble();
break;
case 8:
m_yellow_highlights = parameter.toDouble();
break;
case 9:
m_preserve_luminosity = parameter.toBool();
break;
default:
;
}
}
private:
double m_cyan_midtones, m_magenta_midtones, m_yellow_midtones, m_cyan_shadows, m_magenta_shadows, m_yellow_shadows,
m_cyan_highlights, m_magenta_highlights, m_yellow_highlights;
bool m_preserve_luminosity;
};
KisColorBalanceAdjustmentFactory::KisColorBalanceAdjustmentFactory()
: KoColorTransformationFactory("ColorBalance")
{
}
QList< QPair< KoID, KoID > > KisColorBalanceAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisColorBalanceAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisColorBalanceAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisColorBalanceAdjustment::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisColorBalanceAdjustment< float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisColorBalanceAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisColorBalanceAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisColorBalanceAdjustment< quint8, KoBgrTraits < quint8 > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisColorBalanceAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisColorBalanceAdjustment::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
KisColorBalanceMath::KisColorBalanceMath(){}
float KisColorBalanceMath::colorBalanceTransform(float value, float lightness, float shadows, float midtones, float highlights)
{
static const float a = 0.25, b = 0.333, scale = 0.7;
shadows *= CLAMP ((lightness - b) / -a + 0.5, 0, 1) * scale;
midtones *= CLAMP ((lightness - b) / a + 0.5, 0, 1) * CLAMP ((lightness + b - 1) / -a + 0.5, 0, 1) * scale;
highlights *= CLAMP ((lightness + b - 1) / a + 0.5, 0, 1) * scale;
value += shadows;
value += midtones;
value += highlights;
value = CLAMP (value, 0.0, 1.0);
return value;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_desaturate_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_desaturate_adjustment.cpp
index 2d05eb62fa8..6d628eb1910 100644
--- a/krita/plugins/colorspaces/extensions/kis_desaturate_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_desaturate_adjustment.cpp
@@ -1,193 +1,193 @@
/*
* Copyright (c) 2013 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_desaturate_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
#define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v )
#define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v )
template<typename _channel_type_,typename traits>
class KisDesaturateAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisDesaturateAdjustment()
{
}
public:
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float r, g, b, gray;
while (nPixels > 0) {
r = SCALE_TO_FLOAT(src->red);
g = SCALE_TO_FLOAT(src->green);
b = SCALE_TO_FLOAT(src->blue);
// http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/
switch(m_type) {
case 0: // lightness
{
gray = (qMax(qMax(r, g), b) + qMin(qMin(r, g), b)) / 2;
break;
}
case 1: // luminosity BT 709
{
gray = r * 0.2126 + g * 0.7152 + b * 0.0722;
break;
}
case 2: // luminosity BT 601
{
gray = r * 0.299 + g * 0.587 + b * 0.114;
break;
}
case 3: // average
{
gray = (r + g + b) / 2;
break;
}
case 4: // min
{
gray = qMin(qMin(r, g), b);
break;
}
case 5: // min
{
gray = qMax(qMax(r, g), b);
break;
}
default:
gray = 0;
}
dst->red = SCALE_FROM_FLOAT(gray);
dst->green = SCALE_FROM_FLOAT(gray);
dst->blue = SCALE_FROM_FLOAT(gray);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "type";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "type") {
return 0;
}
return -1;
}
/**
* name - "type":
* 0: lightness
* 1: luminosity
* 2: average
*/
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
m_type = parameter.toDouble();
break;
default:
;
}
}
private:
int m_type;
};
KisDesaturateAdjustmentFactory::KisDesaturateAdjustmentFactory()
: KoColorTransformationFactory("desaturate_adjustment")
{
}
QList< QPair< KoID, KoID > > KisDesaturateAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisDesaturateAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisDesaturateAdjustment< quint8, KoBgrTraits < quint8 > >();
} else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisDesaturateAdjustment< quint16, KoBgrTraits < quint16 > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisDesaturateAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisDesaturateAdjustment< float, KoRgbTraits < float > >();
}
else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDesaturateAdjustmentFactory::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_dodgehighlights_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_dodgehighlights_adjustment.cpp
index 94ea1b12976..4d6648231d8 100644
--- a/krita/plugins/colorspaces/extensions/kis_dodgehighlights_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_dodgehighlights_adjustment.cpp
@@ -1,140 +1,140 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dodgehighlights_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits>
class KisDodgeHighlightsAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisDodgeHighlightsAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue;
const float factor(1.0 + exposure * (0.33333));
while(nPixels > 0) {
value_red = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red);
value_green = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green);
value_blue = factor * KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue);
dst->red = KoColorSpaceMaths< float, _channel_type_>::scaleToA(value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_>::scaleToA(value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisDodgeHighlightsAdjustmentFactory::KisDodgeHighlightsAdjustmentFactory()
: KoColorTransformationFactory("DodgeHighlights")
{
}
QList< QPair< KoID, KoID > > KisDodgeHighlightsAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisDodgeHighlightsAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeHighlightsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeHighlightsAdjustment::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisDodgeHighlightsAdjustment< float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisDodgeHighlightsAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisDodgeHighlightsAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisDodgeHighlightsAdjustment< quint8, KoBgrTraits < quint8 > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeHighlightsAdjustment::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeHighlightsAdjustment::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_dodgemidtones_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_dodgemidtones_adjustment.cpp
index 0df4a1c72f6..58ca43601ea 100644
--- a/krita/plugins/colorspaces/extensions/kis_dodgemidtones_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_dodgemidtones_adjustment.cpp
@@ -1,140 +1,140 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dodgemidtones_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits >
class KisDodgeMidtonesAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisDodgeMidtonesAdjustment(){}
public:
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue;
const float factor(1.0/(1.0 + exposure));
while(nPixels > 0) {
value_red = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red), factor);
value_green = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green), factor);
value_blue = pow((float)KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue), factor);
dst->red = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisDodgeMidtonesAdjustmentFactory::KisDodgeMidtonesAdjustmentFactory()
: KoColorTransformationFactory("DodgeMidtones")
{
}
QList< QPair< KoID, KoID > > KisDodgeMidtonesAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisDodgeMidtonesAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeMidtonesAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeMidtonesAdjustmentFactory::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisDodgeMidtonesAdjustment< float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisDodgeMidtonesAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if(colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisDodgeMidtonesAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if(colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisDodgeMidtonesAdjustment< quint8, KoBgrTraits < quint8 > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeMidtonesAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeMidtonesAdjustmentFactory::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_dodgeshadows_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_dodgeshadows_adjustment.cpp
index ac227f37217..ad3bc6d2b00 100644
--- a/krita/plugins/colorspaces/extensions/kis_dodgeshadows_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_dodgeshadows_adjustment.cpp
@@ -1,143 +1,143 @@
/*
* Copyright (c) 2013 Sahil Nagpal <nagpal.sahil01@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dodgeshadows_adjustment.h"
#include <KoConfig.h>
#include <kis_debug.h>
#include <klocale.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
template<typename _channel_type_, typename traits>
class KisDodgeShadowsAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisDodgeShadowsAdjustment(){}
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float value_red, value_green, value_blue, new_value_red, new_value_green, new_value_blue;
const float factor(exposure * 0.333333);
while (nPixels > 0) {
value_red = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->red);
value_green = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->green);
value_blue = KoColorSpaceMaths<_channel_type_, float>::scaleToA(src->blue);
new_value_red = factor + value_red - factor * value_red;
new_value_green = factor + value_green - factor * value_green;
new_value_blue = factor + value_blue - factor * value_blue;
dst->red = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_red);
dst->green = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_green);
dst->blue = KoColorSpaceMaths< float, _channel_type_ >::scaleToA(new_value_blue);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "exposure";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "exposure")
return 0;
return -1;
}
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
exposure = parameter.toDouble();
break;
default:
;
}
}
private:
float exposure;
};
KisDodgeShadowsAdjustmentFactory::KisDodgeShadowsAdjustmentFactory()
: KoColorTransformationFactory("DodgeShadows")
{
}
QList< QPair< KoID, KoID > > KisDodgeShadowsAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisDodgeShadowsAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeShadowsAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeShadowsAdjustmentFactory::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisDodgeShadowsAdjustment < float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisDodgeShadowsAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisDodgeShadowsAdjustment< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisDodgeShadowsAdjustment< quint8, KoBgrTraits < quint8 > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisDodgeShadowsAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisDodgeShadowsAdjustmentFactory::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/colorspaces/extensions/kis_hsv_adjustment.cpp b/krita/plugins/colorspaces/extensions/kis_hsv_adjustment.cpp
index 49484d0894c..09e044d7566 100644
--- a/krita/plugins/colorspaces/extensions/kis_hsv_adjustment.cpp
+++ b/krita/plugins/colorspaces/extensions/kis_hsv_adjustment.cpp
@@ -1,271 +1,271 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2
* of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_hsv_adjustment.h"
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#include <kis_debug.h>
#include <klocale.h>
#include <KoColorConversions.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorTransformation.h>
#include <KoID.h>
#define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v )
#define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v )
template<typename _channel_type_>
void clamp(float* r, float* g, float* b);
#define FLOAT_CLAMP( v ) * v = (*v < 0.0) ? 0.0 : ( (*v>1.0) ? 1.0 : *v )
template<>
void clamp<quint8>(float* r, float* g, float* b)
{
FLOAT_CLAMP(r);
FLOAT_CLAMP(g);
FLOAT_CLAMP(b);
}
template<>
void clamp<quint16>(float* r, float* g, float* b)
{
FLOAT_CLAMP(r);
FLOAT_CLAMP(g);
FLOAT_CLAMP(b);
}
#ifdef HAVE_OPENEXR
template<>
void clamp<half>(float* r, float* g, float* b)
{
Q_UNUSED(r);
Q_UNUSED(g);
Q_UNUSED(b);
}
#endif
template<>
void clamp<float>(float* r, float* g, float* b)
{
Q_UNUSED(r);
Q_UNUSED(g);
Q_UNUSED(b);
}
template<typename _channel_type_,typename traits>
class KisHSVAdjustment : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisHSVAdjustment()
{
}
public:
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const
{
const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
float h, s, v, r, g, b;
while (nPixels > 0) {
if (m_colorize) {
h = m_adj_h * 360;
if (h >= 360.0) h = 0;
s = m_adj_s;
r = SCALE_TO_FLOAT(src->red);
g = SCALE_TO_FLOAT(src->green);
b = SCALE_TO_FLOAT(src->blue);
float luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
if (m_adj_v > 0) {
luminance *= (1.0 - m_adj_v);
luminance += 1.0 - (1.0 - m_adj_v);
}
else if (m_adj_v < 0 ){
luminance *= (m_adj_v + 1.0);
}
v = luminance;
HSLToRGB(h, s, v, &r, &g, &b);
}
else {
if (m_type == 0) {
RGBToHSV(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v);
h += m_adj_h * 180;
if (h > 360) h -= 360;
if (h < 0) h += 360;
s += m_adj_s;
v += m_adj_v;
HSVToRGB(h, s, v, &r, &g, &b);
}
else {
RGBToHSL(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v);
h += m_adj_h * 180;
if (h > 360) h -= 360;
if (h < 0) h += 360;
s *= (m_adj_s + 1.0);
if (s < 0.0) s = 0.0;
if (s > 1.0) s = 1.0;
if (m_adj_v < 0)
v *= (m_adj_v + 1.0);
else
v += (m_adj_v * (1.0 - v));
HSLToRGB(h, s, v, &r, &g, &b);
}
}
clamp< _channel_type_ >(&r, &g, &b);
dst->red = SCALE_FROM_FLOAT(r);
dst->green = SCALE_FROM_FLOAT(g);
dst->blue = SCALE_FROM_FLOAT(b);
dst->alpha = src->alpha;
--nPixels;
++src;
++dst;
}
}
virtual QList<QString> parameters() const
{
QList<QString> list;
list << "h" << "s" << "v" << "type" << "colorize";
return list;
}
virtual int parameterId(const QString& name) const
{
if (name == "h") {
return 0;
} else if (name == "s") {
return 1;
} else if (name == "v") {
return 2;
} else if (name == "type") {
return 3;
} else if (name == "colorize") {
return 4;
}
return -1;
}
/**
* name - "h", "s" or "v"
* (h)ue in range <-1.0, 1.0> ( for user, show as -180, 180 or 0, 360 for colorize)
* (s)aturation in range <-1.0, 1.0> ( for user, show -100, 100, or 0, 100 for colorize)
* (v)alue in range <-1.0, 1.0> (for user, show -100, 100)
*/
virtual void setParameter(int id, const QVariant& parameter)
{
switch(id)
{
case 0:
m_adj_h = parameter.toDouble();
break;
case 1:
m_adj_s = parameter.toDouble();
break;
case 2:
m_adj_v = parameter.toDouble();
break;
case 3:
m_type = parameter.toDouble();
break;
case 4:
m_colorize = parameter.toBool();
break;
default:
;
}
}
private:
double m_adj_h, m_adj_s, m_adj_v;
int m_type;
bool m_colorize;
};
KisHSVAdjustmentFactory::KisHSVAdjustmentFactory()
: KoColorTransformationFactory("hsv_adjustment")
{
}
QList< QPair< KoID, KoID > > KisHSVAdjustmentFactory::supportedModels() const
{
QList< QPair< KoID, KoID > > l;
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
return l;
}
KoColorTransformation* KisHSVAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
{
KoColorTransformation * adj;
if (colorSpace->colorModelId() != RGBAColorModelID) {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
return 0;
}
if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
adj = new KisHSVAdjustment< quint8, KoBgrTraits < quint8 > >();
} else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
adj = new KisHSVAdjustment< quint16, KoBgrTraits < quint16 > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
adj = new KisHSVAdjustment< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
adj = new KisHSVAdjustment< float, KoRgbTraits < float > >();
} else {
- kError() << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
return 0;
}
adj->setParameters(parameters);
return adj;
}
diff --git a/krita/plugins/extensions/bigbrother/bigbrother.cc b/krita/plugins/extensions/bigbrother/bigbrother.cc
index 63c64856b56..cc3237c47a6 100644
--- a/krita/plugins/extensions/bigbrother/bigbrother.cc
+++ b/krita/plugins/extensions/bigbrother/bigbrother.cc
@@ -1,247 +1,247 @@
/*
* Copyright (c) 2007 Cyrille Berger (cberger@cberger.net)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "bigbrother.h"
#include <cstdlib>
#include <unistd.h>
#include <kis_action.h>
#include <kpluginfactory.h>
#include <klocale.h>
#include <KoIcon.h>
#include <KoUpdater.h>
#include <KoResourceServerProvider.h>
#include <KoFileDialog.h>
#include <kis_config.h>
#include <kis_cursor.h>
#include <kis_debug.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_resource_server_provider.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <KoPattern.h>
#include <recorder/kis_action_recorder.h>
#include <recorder/kis_macro.h>
#include <recorder/kis_macro_player.h>
#include <recorder/kis_play_info.h>
#include <recorder/kis_recorded_action_factory_registry.h>
#include <recorder/kis_recorded_action.h>
#include <recorder/kis_recorded_action_load_context.h>
#include <recorder/kis_recorded_action_save_context.h>
#include "actionseditor/kis_actions_editor.h"
#include "actionseditor/kis_actions_editor_dialog.h"
#include <QDesktopServices>
#include <QApplication>
K_PLUGIN_FACTORY_WITH_JSON(BigBrotherPluginFactory, "kritabigbrother.json", registerPlugin<BigBrotherPlugin>();)
class RecordedActionSaveContext : public KisRecordedActionSaveContext {
public:
virtual void saveGradient(const KoAbstractGradient* ) {}
virtual void savePattern(const KoPattern* ) {}
};
class RecordedActionLoadContext : public KisRecordedActionLoadContext {
public:
virtual KoAbstractGradient* gradient(const QString& name) const
{
return KoResourceServerProvider::instance()->gradientServer()->resourceByName(name);
}
virtual KoPattern* pattern(const QString& name) const
{
return KoResourceServerProvider::instance()->patternServer()->resourceByName(name);
}
};
BigBrotherPlugin::BigBrotherPlugin(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
, m_recorder(0)
{
if (parent->inherits("KisViewManager")) {
m_view = (KisViewManager*) parent;
KisAction* action = 0;
// Open and play action
action = new KisAction(themedIcon("media-playback-start"), i18n("Open and play..."), this);
addAction("Macro_Open_Play", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotOpenPlay()));
// Open and edit action
action = new KisAction(themedIcon("document-edit"), i18n("Open and edit..."), this);
addAction("Macro_Open_Edit", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotOpenEdit()));
// Start recording action
m_startRecordingMacroAction = new KisAction(themedIcon("media-record"), i18n("Start recording macro"), this);
m_startRecordingMacroAction->setActivationFlags(KisAction::ACTIVE_NODE);
addAction("Recording_Start_Recording_Macro", m_startRecordingMacroAction);
connect(m_startRecordingMacroAction, SIGNAL(triggered()), this, SLOT(slotStartRecordingMacro()));
// Save recorded action
m_stopRecordingMacroAction = new KisAction(themedIcon("media-playback-stop"), i18n("Stop recording actions"), this);
m_stopRecordingMacroAction->setActivationFlags(KisAction::ACTIVE_NODE);
addAction("Recording_Stop_Recording_Macro", m_stopRecordingMacroAction);
connect(m_stopRecordingMacroAction, SIGNAL(triggered()), this, SLOT(slotStopRecordingMacro()));
m_stopRecordingMacroAction->setEnabled(false);
}
}
BigBrotherPlugin::~BigBrotherPlugin()
{
m_view = 0;
delete m_recorder;
}
void BigBrotherPlugin::slotOpenPlay()
{
KisMacro* m = openMacro();
- qDebug() << m;
+ dbgKrita << m;
if (!m) return;
dbgPlugins << "Play the macro";
KoProgressUpdater* updater = m_view->createProgressUpdater();
updater->start(1, i18n("Playing back macro"));
KisMacroPlayer player(m, KisPlayInfo(m_view->image(), m_view->activeNode()), updater->startSubtask());
player.start();
while(player.isRunning())
{
QApplication::processEvents();
}
dbgPlugins << "Finished";
delete m;
}
void BigBrotherPlugin::slotOpenEdit()
{
KUrl url;
KisMacro* m = openMacro(&url);
if (!m) return;
KisActionsEditorDialog aed(m_view->mainWindow());
aed.actionsEditor()->setMacro(m);
if (aed.exec() == QDialog::Accepted) {
saveMacro(m, url);
}
delete m;
}
void BigBrotherPlugin::slotStartRecordingMacro()
{
dbgPlugins << "Start recording macro";
if (m_recorder) return;
// Alternate actions
m_startRecordingMacroAction->setEnabled(false);
m_stopRecordingMacroAction->setEnabled(true);
// Create recorder
m_recorder = new KisMacro();
connect(m_view->image()->actionRecorder(), SIGNAL(addedAction(const KisRecordedAction&)),
m_recorder, SLOT(addAction(const KisRecordedAction&)));
}
void BigBrotherPlugin::slotStopRecordingMacro()
{
dbgPlugins << "Stop recording macro";
if (!m_recorder) return;
// Alternate actions
m_startRecordingMacroAction->setEnabled(true);
m_stopRecordingMacroAction->setEnabled(false);
// Save the macro
saveMacro(m_recorder, KUrl());
// Delete recorder
delete m_recorder;
m_recorder = 0;
}
KisMacro* BigBrotherPlugin::openMacro(KUrl* url)
{
Q_UNUSED(url);
QStringList mimeFilter;
mimeFilter << "*.krarec|Recorded actions (*.krarec)";
KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFile, "OpenDocument");
dialog.setCaption(i18n("Open Macro"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setNameFilter(i18n("Recorded actions (*.krarec)"));
QString filename = dialog.url();
RecordedActionLoadContext loadContext;
if (!filename.isNull()) {
QDomDocument doc;
QFile f(filename);
if (f.exists()) {
dbgPlugins << f.open(QIODevice::ReadOnly);
QString err;
int line, col;
if (!doc.setContent(&f, &err, &line, &col)) {
// TODO error message
dbgPlugins << err << " line = " << line << " col = " << col;
f.close();
return 0;
}
f.close();
QDomElement docElem = doc.documentElement();
if (!docElem.isNull() && docElem.tagName() == "RecordedActions") {
dbgPlugins << "Load the macro";
KisMacro* m = new KisMacro();
m->fromXML(docElem, &loadContext);
return m;
} else {
// TODO error message
}
} else {
dbgPlugins << "Unexistant file : " << filename;
}
}
return 0;
}
void BigBrotherPlugin::saveMacro(const KisMacro* macro, const KUrl& url)
{
KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "krita/bigbrother");
dialog.setCaption(i18n("Save Macro"));
dialog.setOverrideDir(url.url());
dialog.setNameFilter(i18n("Recorded actions (*.krarec)"));
QString filename = dialog.url();
if (!filename.isNull()) {
QDomDocument doc;
QDomElement e = doc.createElement("RecordedActions");
RecordedActionSaveContext context;
macro->toXML(doc, e, &context);
doc.appendChild(e);
QFile f(filename);
f.open(QIODevice::WriteOnly);
QTextStream stream(&f);
doc.save(stream, 2);
f.close();
}
}
#include "bigbrother.moc"
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector.cpp
index efe8a31c255..3634e623744 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector.cpp
@@ -1,341 +1,341 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_color_selector.h"
#include <cmath>
#include <QHBoxLayout>
#include <QColor>
#include <QPainter>
#include <QMouseEvent>
#include <QTimer>
#include <QPushButton>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include <kglobal.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KoCanvasResourceManager.h>
#include <KoIcon.h>
#include "kis_color_selector_ring.h"
#include "kis_color_selector_triangle.h"
#include "kis_color_selector_simple.h"
#include "kis_color_selector_wheel.h"
#include "kis_color_selector_container.h"
#include "kis_canvas2.h"
#include "kis_signal_compressor.h"
KisColorSelector::KisColorSelector(Configuration conf, QWidget* parent)
: KisColorSelectorBase(parent),
m_ring(0),
m_triangle(0),
m_slider(0),
m_square(0),
m_wheel(0),
m_mainComponent(0),
m_subComponent(0),
m_grabbingComponent(0),
m_blipDisplay(true)
{
init();
updateSettings();
setConfiguration(conf);
}
KisColorSelector::KisColorSelector(QWidget* parent)
: KisColorSelectorBase(parent),
m_ring(0),
m_triangle(0),
m_slider(0),
m_square(0),
m_wheel(0),
m_button(0),
m_mainComponent(0),
m_subComponent(0),
m_grabbingComponent(0),
m_blipDisplay(true)
{
init();
updateSettings();
}
KisColorSelectorBase* KisColorSelector::createPopup() const
{
KisColorSelectorBase* popup = new KisColorSelector(0);
popup->setColor(m_lastRealColor);
return popup;
}
void KisColorSelector::setConfiguration(Configuration conf)
{
m_configuration = conf;
if(m_mainComponent!=0) {
Q_ASSERT(m_subComponent!=0);
m_mainComponent->setGeometry(0, 0, 0, 0);
m_subComponent->setGeometry(0, 0, 0, 0);
m_mainComponent->disconnect();
m_subComponent->disconnect();
}
switch (m_configuration.mainType) {
case Square:
m_mainComponent=m_square;
break;
case Wheel:
m_mainComponent=m_wheel;
break;
case Triangle:
m_mainComponent=m_triangle;
break;
default:
Q_ASSERT(false);
}
switch (m_configuration.subType) {
case Ring:
m_subComponent=m_ring;
break;
case Slider:
m_subComponent=m_slider;
break;
default:
Q_ASSERT(false);
}
connect(m_mainComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)),
m_subComponent, SLOT(setParam(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), Qt::UniqueConnection);
connect(m_subComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)),
m_mainComponent, SLOT(setParam(qreal,qreal,qreal,qreal, qreal, qreal, qreal, qreal, qreal)), Qt::UniqueConnection);
connect(m_mainComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection);
connect(m_subComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection);
m_mainComponent->setConfiguration(m_configuration.mainTypeParameter, m_configuration.mainType);
m_subComponent->setConfiguration(m_configuration.subTypeParameter, m_configuration.subType);
QResizeEvent event(QSize(width(), height()), QSize());
resizeEvent(&event);
}
KisColorSelector::Configuration KisColorSelector::configuration() const
{
return m_configuration;
}
void KisColorSelector::updateSettings()
{
KisColorSelectorBase::updateSettings();
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
setConfiguration(Configuration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelector::Configuration().toString())));
}
void KisColorSelector::reset()
{
KisColorSelectorBase::reset();
if (m_mainComponent) {
m_mainComponent->setDirty();
}
if (m_subComponent) {
m_subComponent->setDirty();
}
}
void KisColorSelector::paintEvent(QPaintEvent* e)
{
Q_UNUSED(e);
QPainter p(this);
p.fillRect(0,0,width(),height(),QColor(128,128,128));
p.setRenderHint(QPainter::Antialiasing);
m_mainComponent->paintEvent(&p);
m_subComponent->paintEvent(&p);
}
inline int iconSize(qreal width, qreal height) {
qreal radius = qMin(width, height)/2.;
qreal xm = width/2.;
qreal ym = height/2.;
if(xm>=2*ym || ym>=2*xm)
return qBound<qreal>(5., radius, 32.);
qreal a=-2;
qreal b=2.*(xm+ym);
qreal c=radius*radius-xm*xm-ym*ym;
return qBound<qreal>(5., ((-b+sqrt(b*b-4*a*c))/(2*a)), 32.);
}
void KisColorSelector::resizeEvent(QResizeEvent* e) {
if(m_configuration.subType==Ring) {
m_ring->setGeometry(0,0,width(), height());
if(displaySettingsButton()) {
int size = iconSize(width(), height());
m_button->setGeometry(0, 0, size, size);
}
if(m_configuration.mainType==Triangle) {
m_triangle->setGeometry(width()/2-m_ring->innerRadius(),
height()/2-m_ring->innerRadius(),
m_ring->innerRadius()*2,
m_ring->innerRadius()*2);
}
else {
int size = m_ring->innerRadius()*2/sqrt(2.);
m_square->setGeometry(width()/2-size/2,
height()/2-size/2,
size,
size);
}
}
else {
// type wheel and square
if(m_configuration.mainType==Wheel) {
if(displaySettingsButton()) {
int size = iconSize(width(), height()*0.9);
m_button->setGeometry(0, height()*0.1, size, size);
}
m_mainComponent->setGeometry(0, height()*0.1, width(), height()*0.9);
m_subComponent->setGeometry( 0, 0, width(), height()*0.1);
}
else {
int buttonSize = 0;
if(displaySettingsButton()) {
buttonSize = qBound(20, int(0.1*height()), 32);
m_button->setGeometry(0, 0, buttonSize, buttonSize);
}
if(height()>width()) {
int selectorHeight=height()-buttonSize;
m_mainComponent->setGeometry(0, buttonSize+selectorHeight*0.1, width(), selectorHeight*0.9);
m_subComponent->setGeometry( 0, buttonSize, width(), selectorHeight*0.1);
}
else {
int selectorWidth=width()-buttonSize;
m_mainComponent->setGeometry(buttonSize, height()*0.1, selectorWidth, height()*0.9);
m_subComponent->setGeometry( buttonSize, 0, selectorWidth, height()*0.1);
}
}
}
// reset the currect color after resizing the widget
setColor(m_lastRealColor);
KisColorSelectorBase::resizeEvent(e);
}
void KisColorSelector::mousePressEvent(QMouseEvent* e)
{
e->setAccepted(false);
KisColorSelectorBase::mousePressEvent(e);
if(!e->isAccepted()) {
if(m_mainComponent->wantsGrab(e->x(), e->y()))
m_grabbingComponent=m_mainComponent;
else if(m_subComponent->wantsGrab(e->x(), e->y()))
m_grabbingComponent=m_subComponent;
mouseEvent(e);
e->accept();
}
}
void KisColorSelector::mouseMoveEvent(QMouseEvent* e)
{
KisColorSelectorBase::mouseMoveEvent(e);
mouseEvent(e);
e->accept();
}
void KisColorSelector::mouseReleaseEvent(QMouseEvent* e)
{
e->setAccepted(false);
KisColorSelectorBase::mousePressEvent(e);
if(!e->isAccepted() &&
!(m_lastRealColor == m_currentRealColor)) {
m_lastRealColor = m_currentRealColor;
m_lastColorRole = Acs::buttonToRole(e->button());
updateColor(m_lastRealColor, m_lastColorRole, false);
e->accept();
}
m_grabbingComponent=0;
}
bool KisColorSelector::displaySettingsButton()
{
return dynamic_cast<KisColorSelectorContainer*>(parent());
}
void KisColorSelector::setColor(const KoColor &color)
{
m_mainComponent->setColor(color);
m_subComponent->setColor(color);
m_lastRealColor = color;
m_signalCompressor->start();
}
void KisColorSelector::mouseEvent(QMouseEvent *e)
{
if (m_grabbingComponent && (e->buttons() & Qt::LeftButton || e->buttons() & Qt::RightButton)) {
m_grabbingComponent->mouseEvent(e->x(), e->y());
KoColor color = m_mainComponent->currentColor();
m_currentRealColor = color;
updateColorPreview(color);
Acs::ColorRole role = Acs::buttonsToRole(e->button(), e->buttons());
updateColor(color, role, false);
}
}
void KisColorSelector::init()
{
setAcceptDrops(true);
m_lastColorRole = Acs::Foreground;
m_ring = new KisColorSelectorRing(this);
m_triangle = new KisColorSelectorTriangle(this);
m_slider = new KisColorSelectorSimple(this);
m_square = new KisColorSelectorSimple(this);
m_wheel = new KisColorSelectorWheel(this);
if(displaySettingsButton()) {
m_button = new QPushButton(this);
m_button->setIcon(themedIcon("configure"));
connect(m_button, SIGNAL(clicked()), SIGNAL(settingsButtonClicked()));
}
// a tablet can send many more signals, than a mouse
// this causes many repaints, if updating after every signal.
m_signalCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_INACTIVE, this);
connect(m_signalCompressor, SIGNAL(timeout()), SLOT(update()));
setMinimumSize(40, 40);
}
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_combo_box.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_combo_box.cpp
index f6e06283214..1fe1997bfac 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_combo_box.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_combo_box.cpp
@@ -1,242 +1,242 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#include "kis_color_selector_combo_box.h"
#include <QGridLayout>
#include <QPainter>
#include "kis_color_selector.h"
#include "kis_canvas2.h"
class KisColorSelectorComboBoxPrivate : public QWidget {
public:
int spacing;
int selectorSize;
QRect highlightArea;
KisColorSelectorComboBoxPrivate(QWidget* parent) :
QWidget(parent, Qt::Popup),
spacing(20),
selectorSize(100),
highlightArea(-1,-1,0,0)
{
setMouseTracking(true);
QGridLayout* layout = new QGridLayout(this);
layout->setSpacing(spacing);
- //qDebug()<<"Created list";
+ //dbgKrita<<"Created list";
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Triangle, KisColorSelector::Ring, KisColorSelector::SL , KisColorSelector::H), this), 0,0);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Ring, KisColorSelector::SV , KisColorSelector::H), this), 0,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Ring, KisColorSelector::SV2, KisColorSelector::H), this), 0,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::VH, KisColorSelector::hsvS), this), 0,3);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::hsvSH, KisColorSelector::V), this), 0,4);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::SV2, KisColorSelector::H), this), 1,0);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::SV, KisColorSelector::H), this), 1,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::VH, KisColorSelector::hsvS), this), 1,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::hsvSH, KisColorSelector::V), this), 1,3);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Ring, KisColorSelector::SL , KisColorSelector::H), this), 0,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::LH, KisColorSelector::hslS), this), 0,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::hslSH, KisColorSelector::L), this), 0,3);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::SL, KisColorSelector::H), this), 1,0);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::LH, KisColorSelector::hslS), this), 1,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::hslSH, KisColorSelector::L), this), 1,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Ring, KisColorSelector::SI , KisColorSelector::H), this), 0,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::IH, KisColorSelector::hsiS), this), 0,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::hsiSH, KisColorSelector::I), this), 0,3);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::SI, KisColorSelector::H), this), 1,0);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::IH, KisColorSelector::hsiS), this), 1,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::hsiSH, KisColorSelector::I), this), 1,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Ring, KisColorSelector::SY , KisColorSelector::H), this), 0,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::YH, KisColorSelector::hsyS), this), 0,2);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Wheel, KisColorSelector::Slider, KisColorSelector::hsySH, KisColorSelector::Y), this), 0,3);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::SY, KisColorSelector::H), this), 1,0);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::YH, KisColorSelector::hsyS), this), 1,1);
layout->addWidget(new KisColorSelector(KisColorSelector::Configuration(KisColorSelector::Square, KisColorSelector::Slider, KisColorSelector::hsySH, KisColorSelector::Y), this), 1,2);
setList(0);
for(int i=0; i<this->layout()->count(); i++) {
KisColorSelector* item = dynamic_cast<KisColorSelector*>(this->layout()->itemAt(i)->widget());
Q_ASSERT(item);
if(item!=0) {
item->setMaximumSize(selectorSize, selectorSize);
item->setMinimumSize(selectorSize, selectorSize);
item->setMouseTracking(true);
item->setEnabled(false);
item->setColor(KoColor(QColor(255,0,0), item->colorSpace()));
item->setDisplayBlip(false);
}
}
}
void setList(int model){
for(int i=1; i<layout()->count(); i++) {
layout()->itemAt(i)->widget()->hide();
}
if (model==0){
for(int i=1; i<9; i++) {
layout()->itemAt(i)->widget()->show();
}
}
if (model==1){
for(int i=9; i<15; i++) {
layout()->itemAt(i)->widget()->show();
}
}
if (model==2){
for(int i=15; i<21; i++) {
layout()->itemAt(i)->widget()->show();
}
}
if (model==3){
for(int i=21; i<layout()->count(); i++) {
layout()->itemAt(i)->widget()->show();
}
}
}
protected:
void paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(0,0,width(), height(), QColor(128,128,128));
painter.fillRect(highlightArea, palette().highlight());
}
void mouseMoveEvent(QMouseEvent * e)
{
if(rect().contains(e->pos())) {
for(int i=0; i<layout()->count(); i++) {
KisColorSelector* item = dynamic_cast<KisColorSelector*>(layout()->itemAt(i)->widget());
Q_ASSERT(item);
if(layout()->itemAt(i)->widget()->isVisible()==true && item->geometry().adjusted(-spacing/2, -spacing/2, spacing/2, spacing/2).contains(e->pos())) {
QRect oldArea=highlightArea;
highlightArea=item->geometry().adjusted(-spacing/2, -spacing/2, spacing/2, spacing/2);
m_lastActiveConfiguration=item->configuration();
update(highlightArea);
update(oldArea);
}
}
}
else {
highlightArea.setRect(-1,-1,0,0);
}
}
void mousePressEvent(QMouseEvent* e)
{
if(rect().contains(e->pos())) {
KisColorSelectorComboBox* parent = dynamic_cast<KisColorSelectorComboBox*>(this->parent());
Q_ASSERT(parent);
parent->setConfiguration(m_lastActiveConfiguration);
//setList(parent->m_model);
}
- //qDebug()<<"mousepress";
+ //dbgKrita<<"mousepress";
hide();
e->accept();
}
KisColorSelector::Configuration m_lastActiveConfiguration;
};
KisColorSelectorComboBox::KisColorSelectorComboBox(QWidget* parent) :
QComboBox(parent),
m_private(new KisColorSelectorComboBoxPrivate(this)),
m_currentSelector(this)
{
QLayout* layout = new QGridLayout(this);
layout->addWidget(&m_currentSelector);
m_currentSelector.setEnabled(false);
m_currentSelector.setDisplayBlip(false);
m_currentSelector.setColor(KoColor(QColor(255,0,0), m_currentSelector.colorSpace()));
// 30 pixels for the arrow of the combobox
setMinimumSize(m_private->selectorSize+m_private->spacing+30,m_private->selectorSize+m_private->spacing);
m_currentSelector.setMaximumSize(m_private->selectorSize, m_private->selectorSize);
}
KisColorSelectorComboBox::~KisColorSelectorComboBox()
{
}
void KisColorSelectorComboBox::hidePopup()
{
QComboBox::hidePopup();
m_private->hide();
}
void KisColorSelectorComboBox::showPopup()
{
// only show if this is not the popup
QComboBox::showPopup();
m_private->move(mapToGlobal(QPoint(0,0)));
m_private->show();
}
void KisColorSelectorComboBox::setColorSpace(const KoColorSpace *colorSpace)
{
//this is not the popup, but we should set the canvas for all popup selectors
for(int i=0; i<m_private->layout()->count(); i++) {
KisColorSelector* item = dynamic_cast<KisColorSelector*>(m_private->layout()->itemAt(i)->widget());
Q_ASSERT(item);
if(item!=0) {
item->setColorSpace(colorSpace);
}
}
m_currentSelector.setColorSpace(colorSpace);
update();
}
KisColorSelector::Configuration KisColorSelectorComboBox::configuration() const
{
return m_configuration;
}
void KisColorSelectorComboBox::paintEvent(QPaintEvent *e)
{
QComboBox::paintEvent(e);
}
void KisColorSelectorComboBox::setConfiguration(KisColorSelector::Configuration conf)
{
m_configuration=conf;
m_currentSelector.setConfiguration(conf);
m_currentSelector.setColor(KoColor(QColor(255,0,0), m_currentSelector.colorSpace()));
update();
}
void KisColorSelectorComboBox::setList(int model) {
m_private->setList(model);
}
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_simple.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_simple.cpp
index 8c3a5e28884..7cd5ada42b2 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_simple.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_simple.cpp
@@ -1,414 +1,414 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#include "kis_color_selector_simple.h"
#include <QImage>
#include <QPainter>
#include <QColor>
#include <cmath>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include <kglobal.h>
#include "kis_display_color_converter.h"
#include "kis_acs_pixel_cache_renderer.h"
KisColorSelectorSimple::KisColorSelectorSimple(KisColorSelector *parent) :
KisColorSelectorComponent(parent),
m_lastClickPos(-1,-1)
{
}
KoColor KisColorSelectorSimple::selectColor(int x, int y)
{
m_lastClickPos.setX(x/qreal(width()));
m_lastClickPos.setY(y/qreal(height()));
qreal xRel = x/qreal(width());
qreal yRel = 1.-y/qreal(height());
qreal relPos;
if(height()>width())
relPos = 1.-y/qreal(height());
else
relPos = x/qreal(width());
switch (m_parameter) {
case KisColorSelector::H:
emit paramChanged(relPos, -1, -1, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsvS:
emit paramChanged(-1, relPos, -1, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hslS:
emit paramChanged(-1, -1, -1, relPos, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsiS:
emit paramChanged(-1, -1, -1, -1, -1, relPos, -1, -1, -1);
break;
case KisColorSelector::hsyS:
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, relPos, -1);
break;
case KisColorSelector::V:
emit paramChanged(-1, -1, relPos, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::L:
emit paramChanged(-1, -1, -1, -1, relPos, -1, -1, -1, -1);
break;
case KisColorSelector::I:
emit paramChanged(-1, -1, -1, -1, -1, -1, relPos, -1, -1);
break;
case KisColorSelector::Y:
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, -1, relPos);
break;
case KisColorSelector::SL:
emit paramChanged(-1, -1, -1, xRel, yRel, -1, -1, -1, -1);
break;
case KisColorSelector::SI:
emit paramChanged(-1, -1, -1, -1, -1, xRel, yRel, -1, -1);
break;
case KisColorSelector::SY:
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, xRel, yRel);
break;
case KisColorSelector::SV2:
case KisColorSelector::SV:
emit paramChanged(-1, xRel, yRel, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsvSH:
emit paramChanged(xRel, yRel, -1, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hslSH:
emit paramChanged(xRel, -1, -1, yRel, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsiSH:
emit paramChanged(xRel, -1, -1, -1, -1, yRel, -1, -1, -1);
break;
case KisColorSelector::hsySH:
emit paramChanged(xRel, -1, -1, -1, -1, -1, -1, yRel, -1);
break;
case KisColorSelector::VH:
emit paramChanged(xRel, -1, yRel, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::LH:
emit paramChanged(xRel, -1, -1, -1, yRel, -1, -1, -1, -1);
break;
case KisColorSelector::IH:
emit paramChanged(xRel, -1, -1, -1, -1, -1, yRel, -1, -1);
break;
case KisColorSelector::YH:
emit paramChanged(xRel, -1, -1, -1, -1, -1, -1, -1, yRel);
break;
}
emit update();
return colorAt(x, y);
}
void KisColorSelectorSimple::setColor(const KoColor &color)
{
qreal hsvH, hsvS, hsvV;
qreal hslH, hslS, hslL;
qreal hsiH, hsiS, hsiI;
qreal hsyH, hsyS, hsyY;
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
R = cfg.readEntry("lumaR", 0.2126);
G = cfg.readEntry("lumaG", 0.7152);
B = cfg.readEntry("lumaB", 0.0722);
m_parent->converter()->getHsvF(color, &hsvH, &hsvS, &hsvV);
m_parent->converter()->getHslF(color, &hslH, &hslS, &hslL);
//here we add our convertor options
m_parent->converter()->getHsiF(color, &hsiH, &hsiS, &hsiI);
m_parent->converter()->getHsyF(color, &hsyH, &hsyS, &hsyY, R, G, B);
//workaround, for some reason the HSI and HSY algorithms are fine, but they don't seem to update the selectors properly.
hsiH=hslH;
hsyH=hslH;
switch (m_parameter) {
case KisColorSelector::SL:
m_lastClickPos.setX(hslS);
m_lastClickPos.setY(1 - hslL);
emit paramChanged(-1, -1, -1, hslS, hslL, -1, -1, -1, -1);
break;
case KisColorSelector::SI:
m_lastClickPos.setX(hsiS);
m_lastClickPos.setY(1 - hsiI);
emit paramChanged(-1, -1, -1, -1, -1, hsiS, hsiI, -1, -1);
break;
case KisColorSelector::SY:
m_lastClickPos.setX(hsyS);
m_lastClickPos.setY(1 - hsyY);
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, hsyS, hsyY);
break;
case KisColorSelector::LH:
m_lastClickPos.setX(qBound<qreal>(0., hslH, 1.));
m_lastClickPos.setY(1 - hslL);
emit paramChanged(hslH, -1, -1, -1, hslL, -1, -1, -1, -1);
break;
case KisColorSelector::SV:
m_lastClickPos.setX(hsvS);
m_lastClickPos.setY(1 - hsvV);
emit paramChanged(-1, hsvS, hsvV, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::SV2: {
qreal xRel = hsvS;
qreal yRel = 0.5;
if(xRel != 1.0)
yRel = 1.0 - qBound<qreal>(0.0, (hsvV - xRel) / (1.0 - xRel), 1.0);
m_lastClickPos.setX(xRel);
m_lastClickPos.setY(yRel);
emit paramChanged(-1, -1, -1, xRel, yRel, -1, -1, -1, -1);
break;
}
case KisColorSelector::VH:
m_lastClickPos.setX(qBound<qreal>(0., hsvH, 1.));
m_lastClickPos.setY(1 - hsvV);
emit paramChanged(hsvH, -1, hsvV, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::IH:
m_lastClickPos.setX(qBound<qreal>(0., hsiH, 1.));
m_lastClickPos.setY(1 - hsiI);
emit paramChanged(hsiH, -1, -1, -1, -1, -1, hsiI, -1, -1);
break;
case KisColorSelector::YH:
m_lastClickPos.setX(qBound<qreal>(0., hsyH, 1.));
m_lastClickPos.setY(1 - hsyY);
emit paramChanged(hsyH, -1, -1, -1, -1, -1, -1, -1, hsyY);
break;
case KisColorSelector::hsvSH:
m_lastClickPos.setX(qBound<qreal>(0., hsvH, 1.));
m_lastClickPos.setY(1 - hsvS);
emit paramChanged(hsvH, hsvS, -1, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hslSH:
m_lastClickPos.setX(qBound<qreal>(0., hslH, 1.));
m_lastClickPos.setY(1 - hslS);
emit paramChanged(hslH, -1, -1, hslS, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsiSH:
m_lastClickPos.setX(qBound<qreal>(0., hsiH, 1.));
m_lastClickPos.setY(1 - hsiS);
emit paramChanged(hsiH, -1, -1, hsiS, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsySH:
m_lastClickPos.setX(qBound<qreal>(0., hsyH, 1.));
m_lastClickPos.setY(1 - hsyS);
emit paramChanged(hsyH, -1, -1, hsyS, -1, -1, -1, -1, -1);
break;
case KisColorSelector::L:
m_lastClickPos.setX(qBound<qreal>(0., hslL, 1.));
emit paramChanged(-1, -1, -1, -1, hslL, -1, -1, -1, -1);
break;
case KisColorSelector::I:
m_lastClickPos.setX(qBound<qreal>(0., hsiI, 1.));
emit paramChanged(-1, -1, -1, -1, -1, -1, hsiI, -1, -1);
break;
case KisColorSelector::V:
m_lastClickPos.setX(hsvV);
emit paramChanged(-1, -1, hsvV, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::Y:
m_lastClickPos.setX(qBound<qreal>(0., hsyY, 1.));
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, -1, hsyY);
break;
case KisColorSelector::hsvS:
m_lastClickPos.setX( hsvS );
emit paramChanged(-1, hsvS, -1, -1, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hslS:
m_lastClickPos.setX( hslS );
emit paramChanged(-1, -1, -1, hslS, -1, -1, -1, -1, -1);
break;
case KisColorSelector::hsiS:
m_lastClickPos.setX( hsiS );
emit paramChanged(-1, -1, -1, -1, -1, hsiS, -1, -1, -1);
break;
case KisColorSelector::hsyS:
m_lastClickPos.setX( hsyS );
emit paramChanged(-1, -1, -1, -1, -1, -1, -1, hsyS, -1);
break;
case KisColorSelector::H:
m_lastClickPos.setX(qBound<qreal>(0., hsvH, 1.));
emit paramChanged(hsvH, -1, -1, -1, -1, -1, -1, -1, -1);
break;
default:
Q_ASSERT(false);
break;
}
emit update();
//Workaround for bug 317648
setLastMousePosition((m_lastClickPos.x()*width()), (m_lastClickPos.y()*height()));
}
void KisColorSelectorSimple::paint(QPainter* painter)
{
if(isDirty()) {
KisPaintDeviceSP realPixelCache;
QPoint pixelCacheOffset;
Acs::PixelCacheRenderer::render(this,
m_parent->converter(),
QRect(0, 0, width(), height()),
realPixelCache,
m_pixelCache,
pixelCacheOffset);
// if (!pixelCacheOffset.isNull()) {
-// qWarning() << "WARNING: offset of the rectangle selector is not null!";
+// warnKrita << "WARNING: offset of the rectangle selector is not null!";
// }
}
painter->drawImage(0,0, m_pixelCache);
// draw blip
if(m_lastClickPos!=QPointF(-1,-1) && m_parent->displayBlip()) {
switch (m_parameter) {
case KisColorSelector::H:
case KisColorSelector::hsvS:
case KisColorSelector::hslS:
case KisColorSelector::hsiS:
case KisColorSelector::hsyS:
case KisColorSelector::V:
case KisColorSelector::L:
case KisColorSelector::I:
case KisColorSelector::Y:
if(width()>height()) {
painter->setPen(QColor(0,0,0));
painter->drawLine(m_lastClickPos.x()*width()-1, 0, m_lastClickPos.x()*width()-1, height());
painter->setPen(QColor(255,255,255));
painter->drawLine(m_lastClickPos.x()*width()+1, 0, m_lastClickPos.x()*width()+1, height());
}
else {
painter->setPen(QColor(0,0,0));
painter->drawLine(0, m_lastClickPos.x()*height()-1, width(), m_lastClickPos.x()*height()-1);
painter->setPen(QColor(255,255,255));
painter->drawLine(0, m_lastClickPos.x()*height()+1, width(), m_lastClickPos.x()*height()+1);
}
break;
case KisColorSelector::SL:
case KisColorSelector::SV:
case KisColorSelector::SV2:
case KisColorSelector::SI:
case KisColorSelector::SY:
case KisColorSelector::hslSH:
case KisColorSelector::hsvSH:
case KisColorSelector::hsiSH:
case KisColorSelector::hsySH:
case KisColorSelector::VH:
case KisColorSelector::LH:
case KisColorSelector::IH:
case KisColorSelector::YH:
painter->setPen(QColor(0,0,0));
painter->drawEllipse(m_lastClickPos.x()*width()-5, m_lastClickPos.y()*height()-5, 10, 10);
painter->setPen(QColor(255,255,255));
painter->drawEllipse(m_lastClickPos.x()*width()-4, m_lastClickPos.y()*height()-4, 8, 8);
break;
}
}
}
KoColor KisColorSelectorSimple::colorAt(int x, int y)
{
qreal xRel = x/qreal(width());
qreal yRel = 1.-y/qreal(height());
qreal relPos;
if(height()>width())
relPos = 1.-y/qreal(height());
else
relPos = x/qreal(width());
KoColor color(Qt::transparent, m_parent->colorSpace());
switch(m_parameter) {
case KisColorSelector::SL:
color = m_parent->converter()->fromHslF(m_hue, xRel, yRel);
break;
case KisColorSelector::SV:
color = m_parent->converter()->fromHsvF(m_hue, xRel, yRel);
break;
case KisColorSelector::SV2:
color = m_parent->converter()->fromHsvF(m_hue, xRel, xRel + (1.0-xRel)*yRel);
break;
case KisColorSelector::SI:
color = m_parent->converter()->fromHsiF(m_hue, xRel, yRel);
break;
case KisColorSelector::SY:
color = m_parent->converter()->fromHsyF(m_hue, xRel, yRel, R, G, B);
break;
case KisColorSelector::hsvSH:
color = m_parent->converter()->fromHsvF(xRel, yRel, m_value);
break;
case KisColorSelector::hslSH:
color = m_parent->converter()->fromHslF(xRel, yRel, m_lightness);
break;
case KisColorSelector::hsiSH:
color = m_parent->converter()->fromHsiF(xRel, yRel, m_intensity);
break;
case KisColorSelector::hsySH:
color = m_parent->converter()->fromHsyF(xRel, yRel, m_luma, R, G, B);
break;
case KisColorSelector::VH:
color = m_parent->converter()->fromHsvF(xRel, m_hsvSaturation, yRel);
break;
case KisColorSelector::LH:
color = m_parent->converter()->fromHslF(xRel, m_hslSaturation, yRel);
break;
case KisColorSelector::IH:
color = m_parent->converter()->fromHsiF(xRel, m_hsiSaturation, yRel);
break;
case KisColorSelector::YH:
color = m_parent->converter()->fromHsyF(xRel, m_hsySaturation, yRel, R, G, B);
break;
case KisColorSelector::H:
color = m_parent->converter()->fromHsvF(relPos, 1, 1);
break;
case KisColorSelector::hsvS:
color = m_parent->converter()->fromHsvF(m_hue, relPos, m_value);
break;
case KisColorSelector::hslS:
color = m_parent->converter()->fromHslF(m_hue, relPos, m_lightness);
break;
case KisColorSelector::V:
color = m_parent->converter()->fromHsvF(m_hue, m_hsvSaturation, relPos);
break;
case KisColorSelector::L:
color = m_parent->converter()->fromHslF(m_hue, m_hslSaturation, relPos);
break;
case KisColorSelector::hsiS:
color = m_parent->converter()->fromHsiF(m_hue, relPos, m_intensity);
break;
case KisColorSelector::I:
color = m_parent->converter()->fromHsiF(m_hue, m_hsiSaturation, relPos);
break;
case KisColorSelector::hsyS:
color = m_parent->converter()->fromHsyF(m_hue, relPos, m_luma, R, G, B);
break;
case KisColorSelector::Y:
color = m_parent->converter()->fromHsyF(m_hue, m_hsySaturation, relPos, R, G, B);
break;
default:
Q_ASSERT(false);
return color;
}
return color;
}
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.cpp
index f9b8116c8bf..b7170f7e830 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.cpp
@@ -1,183 +1,187 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#include "kis_color_selector_triangle.h"
#include <QPainter>
#include <QMouseEvent>
#include <cmath>
#include "KoColorSpace.h"
#include "kis_display_color_converter.h"
#include "kis_acs_pixel_cache_renderer.h"
KisColorSelectorTriangle::KisColorSelectorTriangle(KisColorSelector* parent) :
KisColorSelectorComponent(parent),
m_lastClickPos(-1,-1)
{
}
bool KisColorSelectorTriangle::containsPointInComponentCoords(int x, int y) const
{
QPoint triangleCoords = widgetToTriangleCoordinates(QPoint(x, y));
if (!m_realPixelCache) return false;
KoColor pixel = Acs::pickColor(m_realPixelCache, triangleCoords);
return pixel.opacityU8() == OPACITY_OPAQUE_U8;
}
void KisColorSelectorTriangle::paint(QPainter* painter)
{
if(isDirty()) {
updatePixelCache();
}
painter->drawImage(width()/2-triangleWidth()/2,
height()/2-triangleHeight()*(2/3.),
m_renderedPixelCache);
if(m_lastClickPos.x()>-0.1 && m_parent->displayBlip()) {
painter->setPen(QColor(0,0,0));
painter->drawEllipse(m_lastClickPos.x()*width()-5, m_lastClickPos.y()*height()-5, 10, 10);
painter->setPen(QColor(255,255,255));
painter->drawEllipse(m_lastClickPos.x()*width()-4, m_lastClickPos.y()*height()-4, 8, 8);
}
}
void KisColorSelectorTriangle::updatePixelCache()
{
int width = triangleWidth() + 1;
int height = triangleHeight();
QPoint pixelCacheOffset;
+ if (m_cachedSize != QSize(width, height) && m_realPixelCache) {
+ m_realPixelCache = 0;
+ }
+
Acs::PixelCacheRenderer::render(this,
m_parent->converter(),
QRect(0, 0, width, height),
m_realPixelCache,
m_renderedPixelCache,
pixelCacheOffset);
// if (!pixelCacheOffset.isNull()) {
-// qWarning() << "WARNING: offset of the triangle selector is not null!";
+// warnKrita << "WARNING: offset of the triangle selector is not null!";
// }
// antialiased border
QPainter gc(&m_renderedPixelCache);
gc.setRenderHint(QPainter::Antialiasing);
gc.setPen(QPen(QColor(0,0,0,128), 2.5));
gc.setCompositionMode(QPainter::CompositionMode_Clear);
gc.drawLine(QPointF(0, triangleHeight()), QPointF((triangleWidth()) / 2.0, 0));
gc.drawLine(QPointF(triangleWidth() / 2.0 + 1.0, 0), QPointF(triangleWidth() + 1, triangleHeight()));
}
KoColor KisColorSelectorTriangle::selectColor(int x, int y)
{
emit update();
QPoint triangleCoords = widgetToTriangleCoordinates(QPoint(x,y));
triangleCoords.setY(qBound(0, triangleCoords.y(), int(triangleHeight())));
int horizontalLineLength = triangleCoords.y()*(2./sqrt(3.));
int horizontalLineStart = triangleWidth()/2.-horizontalLineLength/2.;
int horizontalLineEnd = horizontalLineStart+horizontalLineLength;
triangleCoords.setX(qBound(horizontalLineStart, triangleCoords.x(), horizontalLineEnd));
QPoint widgetCoords = triangleToWidgetCoordinates(triangleCoords);
m_lastClickPos.setX(widgetCoords.x()/qreal(width()));
m_lastClickPos.setY(widgetCoords.y()/qreal(height()));
return colorAt(triangleCoords.x(), triangleCoords.y());
}
void KisColorSelectorTriangle::setColor(const KoColor &color)
{
qreal h, s, v;
m_parent->converter()->getHsvF(color, &h, &s, &v);
qreal y = v * triangleHeight();
qreal horizontalLineLength = y * (2. / sqrt(3.));
qreal horizontalLineStart = 0.5 * (triangleWidth() - horizontalLineLength);
qreal x=s * horizontalLineLength + horizontalLineStart;
QPoint tmp = triangleToWidgetCoordinates(QPoint(x, y));
m_lastClickPos.setX(tmp.x()/qreal(width()));
m_lastClickPos.setY(tmp.y()/qreal(height()));
// Workaround for Bug 287001
setLastMousePosition(tmp.x(), tmp.y());
emit paramChanged(-1, s, v, -1, -1, -1, -1, -1, -1);
emit update();
}
int KisColorSelectorTriangle::triangleWidth() const
{
return triangleHeight()*2/sqrt(3.0);
}
int KisColorSelectorTriangle::triangleHeight() const
{
return height()*3./4.;
}
KoColor KisColorSelectorTriangle::colorAt(int x, int y) const
{
Q_ASSERT(x>=0 && x<=triangleWidth());
Q_ASSERT(y>=0 && y<=triangleHeight());
int triangleHeight = this->triangleHeight();
int horizontalLineLength = y*(2./sqrt(3.));
int horizontalLineStart = triangleWidth()/2.-horizontalLineLength/2.;
int horizontalLineEnd = horizontalLineStart+horizontalLineLength;
if(x<horizontalLineStart || x>horizontalLineEnd || y>triangleHeight)
return KoColor(Qt::transparent, colorSpace());
qreal relativeX = x-horizontalLineStart;
qreal value = (y)/qreal(triangleHeight);
qreal saturation = relativeX/qreal(horizontalLineLength);
return m_parent->converter()->fromHsvF(m_hue, saturation, value);
}
QPoint KisColorSelectorTriangle::widgetToTriangleCoordinates(const QPoint &point) const
{
QPoint triangleTopLeft(width()/2-triangleWidth()/2,
height()/2-triangleHeight()*(2/3.));
QPoint ret=point-triangleTopLeft;
return ret;
}
QPoint KisColorSelectorTriangle::triangleToWidgetCoordinates(const QPoint &point) const
{
QPoint triangleTopLeft(width()/2.-triangleWidth()/2.,
height()/2.-triangleHeight()*(2./3.));
QPoint ret=triangleTopLeft+point;
return ret;
}
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.h b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.h
index 13f423b0f09..dd605761dfe 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.h
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_color_selector_triangle.h
@@ -1,60 +1,60 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#ifndef KIS_COLOR_SELECTOR_TRIANGLE_H
#define KIS_COLOR_SELECTOR_TRIANGLE_H
#include "kis_color_selector_component.h"
#include "kis_paint_device.h"
-
+#include <QSize>
#include <QImage>
namespace Acs {
class PixelCacheRenderer;
}
class KisColorSelectorTriangle : public KisColorSelectorComponent
{
Q_OBJECT
public:
explicit KisColorSelectorTriangle(KisColorSelector* parent);
void setColor(const KoColor &color);
protected:
void paint(QPainter*);
KoColor selectColor(int x, int y);
bool containsPointInComponentCoords(int x, int y) const;
private:
friend class Acs::PixelCacheRenderer;
KoColor colorAt(int x, int y) const;
private:
int triangleWidth() const;
int triangleHeight() const;
void updatePixelCache();
QPoint widgetToTriangleCoordinates(const QPoint& point) const;
QPoint triangleToWidgetCoordinates(const QPoint& point) const;
private:
QImage m_renderedPixelCache;
KisPaintDeviceSP m_realPixelCache;
-
+ QSize m_cachedSize;
QPointF m_lastClickPos;
};
#endif // KIS_COLOR_SELECTOR_TRIANGLE_H
diff --git a/krita/plugins/extensions/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp b/krita/plugins/extensions/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp
index 6b300755e40..575efb771d4 100644
--- a/krita/plugins/extensions/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp
+++ b/krita/plugins/extensions/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp
@@ -1,304 +1,304 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
* Copyright (c) 2008 Martin Renold <martinxyz@gmx.ch>
* Copyright (c) 2009 Ilya Portnov <nomail>
*
* This class is based on "lib/colorchanger.hpp" from MyPaint (mypaint.intilinux.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_my_paint_shade_selector.h"
#include <cmath>
#include <cstdlib>
#include <QImage>
#include <QColor>
#include <QPainter>
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QtGlobal>
#include <QTimer>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include <kglobal.h>
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoColor.h"
#include "KoCanvasResourceManager.h"
#include "kis_paint_device.h"
#include "kis_painter.h"
#include "kis_display_color_converter.h"
inline int sqr(int x);
inline qreal sqr2(qreal x);
inline int signedSqr(int x);
KisMyPaintShadeSelector::KisMyPaintShadeSelector(QWidget *parent) :
KisColorSelectorBase(parent),
m_updateTimer(new QTimer(this))
{
setAcceptDrops(true);
updateSettings();
setMinimumSize(80, 80);
setColor(KoColor(Qt::red, colorSpace()));
m_updateTimer->setInterval(1);
m_updateTimer->setSingleShot(true);
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
}
void KisMyPaintShadeSelector::paintEvent(QPaintEvent *) {
// Hint to the casual reader: some of the calculation here do not
// what Martin Renold originally intended. Not everything here will make sense.
// It does not matter in the end, as long as the result looks good.
// This selector was ported from MyPaint in 2010
if (m_cachedColorSpace != colorSpace()) {
m_realPixelCache = new KisPaintDevice(colorSpace());
m_realCircleBorder = new KisPaintDevice(colorSpace());
m_cachedColorSpace = colorSpace();
}
else {
m_realPixelCache->clear();
m_realCircleBorder->clear();
}
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
int size = qMin(width(), height());
int s_radius = size/2.6;
for (int x=0; x<width(); x++) {
for (int y=0; y<height(); y++) {
float v_factor = 0.6f;
float s_factor = 0.6f;
float v_factor2 = 0.013f;
float s_factor2 = 0.013f;
int stripe_width = 15*size/255.;
float h = 0;
float s = 0;
float v = 0;
int dx = x-width()/2;
int dy = y-height()/2;
int diag = sqrt(2.0)*size/2;
int dxs, dys;
if (dx > 0)
dxs = dx - stripe_width;
else
dxs = dx + stripe_width;
if (dy > 0)
dys = dy - stripe_width;
else
dys = dy + stripe_width;
qreal r = std::sqrt(qreal(sqr(dxs)+sqr(dys)));
if (qMin(abs(dx), abs(dy)) < stripe_width) {
// horizontal and vertical lines
dx = (dx/qreal(width()))*255;
dy = (dy/qreal(height()))*255;
h = 0;
// x-axis = value, y-axis = saturation
v = dx*v_factor + signedSqr(dx)*v_factor2;
s = - (dy*s_factor + signedSqr(dy)*s_factor2);
// but not both at once
if (std::abs(dx) > std::abs(dy)) {
// horizontal stripe
s = 0.0;
} else {
// vertical stripe
v = 0.0;
}
}
else if (r < s_radius+1) {
// hue
if (dx > 0)
h = 90*sqr2(r/s_radius);
else
h = 360 - 90*sqr2(r/s_radius);
s = 256*(atan2f(std::abs(dxs),dys)/M_PI) - 128;
if (r > s_radius) {
// antialiasing boarder
qreal aaFactor = r-floor(r); // part after the decimal point
aaFactor = 1-aaFactor;
qreal fh = m_colorH + h/360.0;
qreal fs = m_colorS + s/255.0;
qreal fv = m_colorV + v/255.0;
fh -= floor(fh);
fs = qBound(qreal(0.0), fs, qreal(1.0));
fv = qBound(qreal(0.01), fv, qreal(1.0));
KoColor color;
//KoColor color = converter()->fromHsvF(fh, fs, fv);
if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv, R, G, B);}
- else{qDebug()<<"MyPaint Color selector don't work right.";
+ else{dbgKrita<<"MyPaint Color selector don't work right.";
color = converter()->fromHsvF(fh, fs, fv);}
-//qDebug()<<color->toQcolor();
+//dbgKrita<<color->toQcolor();
color.setOpacity(aaFactor);
Acs::setColor(m_realCircleBorder, QPoint(x, y), color);
h = 180 + 180*atan2f(dys,-dxs)/M_PI;
v = 255*(r-s_radius)/(diag-s_radius) - 128;
}
}
else {
// background (hue+darkness gradient)
h = 180 + 180*atan2f(dys,-dxs)/M_PI;
v = 255*(r-s_radius)/(diag-s_radius) - 128;
}
qreal fh = m_colorH + h/360.0;
qreal fs = m_colorS + s/255.0;
qreal fv = m_colorV + v/255.0;
fh -= floor(fh);
fs = qBound(qreal(0.0), fs, qreal(1.0));
fv = qBound(qreal(0.01), fv, qreal(1.0));
KoColor color;
//KoColor color = converter()->fromHsvF(fh, fs, fv);
if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);}
else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);}
else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);}
else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv);}
- else{qDebug()<<"MyPaint Color selector don't work right.";
+ else{dbgKrita<<"MyPaint Color selector don't work right.";
color = converter()->fromHsvF(fh, fs, fv);}
Acs::setColor(m_realPixelCache, QPoint(x, y), color);
}
}
KisPainter gc(m_realPixelCache);
gc.bitBlt(QPoint(0,0), m_realCircleBorder, rect());
QPainter painter(this);
QImage renderedImage = converter()->toQImage(m_realPixelCache);
painter.drawImage(0, 0, renderedImage);
}
void KisMyPaintShadeSelector::mousePressEvent(QMouseEvent* e)
{
e->setAccepted(false);
KisColorSelectorBase::mousePressEvent(e);
}
void KisMyPaintShadeSelector::mouseMoveEvent(QMouseEvent *e)
{
if(rect().contains(e->pos())) {
KoColor color(Acs::pickColor(m_realPixelCache, e->pos()));
this->updateColorPreview(color);
}
KisColorSelectorBase::mouseMoveEvent(e);
}
void KisMyPaintShadeSelector::mouseReleaseEvent(QMouseEvent *e)
{
e->setAccepted(false);
KisColorSelectorBase::mouseReleaseEvent(e);
if(!e->isAccepted()) {
KoColor color(Acs::pickColor(m_realPixelCache, e->pos()));
Acs::ColorRole role = Acs::buttonToRole(e->button());
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
bool onRightClick = cfg.readEntry("shadeSelectorUpdateOnRightClick", false);
bool onLeftClick = cfg.readEntry("shadeSelectorUpdateOnLeftClick", false);
bool explicitColorReset =
(e->button() == Qt::LeftButton && onLeftClick) ||
(e->button() == Qt::RightButton && onRightClick);
this->updateColor(color, role, explicitColorReset);
e->accept();
}
}
KisColorSelectorBase* KisMyPaintShadeSelector::createPopup() const
{
KisColorSelectorBase* popup = new KisMyPaintShadeSelector(0);
popup->setColor(m_lastRealColor);
return popup;
}
void KisMyPaintShadeSelector::setColor(const KoColor &color) {
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV");
R = cfg.readEntry("lumaR", 0.2126);
G = cfg.readEntry("lumaG", 0.7152);
B = cfg.readEntry("lumaB", 0.0722);
if(shadeMyPaintType=="HSV"){this->converter()->getHsvF(color, &m_colorH, &m_colorS, &m_colorV);}
if(shadeMyPaintType=="HSL"){this->converter()->getHslF(color, &m_colorH, &m_colorS, &m_colorV);}
if(shadeMyPaintType=="HSI"){this->converter()->getHsiF(color, &m_colorH, &m_colorS, &m_colorV);}
if(shadeMyPaintType=="HSY"){this->converter()->getHsyF(color, &m_colorH, &m_colorS, &m_colorV, R, G, B);}
m_lastRealColor = color;
this->updateColorPreview(color);
m_updateTimer->start();
}
void KisMyPaintShadeSelector::canvasResourceChanged(int key, const QVariant &v)
{
if(m_colorUpdateAllowed==false)
return;
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
bool onForeground = cfg.readEntry("shadeSelectorUpdateOnForeground", false);
bool onBackground = cfg.readEntry("shadeSelectorUpdateOnBackground", true);
if ((key == KoCanvasResourceManager::ForegroundColor && onForeground) ||
(key == KoCanvasResourceManager::BackgroundColor && onBackground)) {
setColor(v.value<KoColor>());
}
}
inline int sqr(int x) {
return x*x;
}
inline qreal sqr2(qreal x) {
return (x*x)/2+x/2;
}
inline int signedSqr(int x) {
int sign = x>0?1:-1;
return x*x*sign;
}
diff --git a/krita/plugins/extensions/dockers/colorslider/kis_color_slider_widget.cpp b/krita/plugins/extensions/dockers/colorslider/kis_color_slider_widget.cpp
index 96f9dd85ba0..1e4325b6325 100644
--- a/krita/plugins/extensions/dockers/colorslider/kis_color_slider_widget.cpp
+++ b/krita/plugins/extensions/dockers/colorslider/kis_color_slider_widget.cpp
@@ -1,483 +1,483 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2014 Wolthera van Hövell <griffinvalley@gmail.com>
* Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_color_slider_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QBitArray>
#include <QSpacerItem>
#include <klocale.h>
#include <kconfiggroup.h>
#include <kconfig.h>
#include <kglobal.h>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include "kis_color_slider_input.h"
#include <KoColorProfile.h>
#include "kis_debug.h"
#include "kis_signal_compressor.h"
//#include "kis_color_space_selector.h"
KisColorSliderWidget::KisColorSliderWidget(KoColorDisplayRendererInterface *displayRenderer, QWidget* parent, KisCanvas2* canvas, QBitArray SlidersConfigArray)
: QWidget(parent)
//, m_colorSpace(0)
//, m_customColorSpaceSelected(false)
, m_updateCompressor(new KisSignalCompressor(10, KisSignalCompressor::POSTPONE, this))
, m_displayRenderer(displayRenderer)
, m_canvas(canvas)
{
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0,0,0,0);
m_layout->setSpacing(1);
m_updateAllowed = true;
connect(m_updateCompressor, SIGNAL(timeout()), SLOT(updateTimeout()));
m_configCompressor = new KisSignalCompressor(10, KisSignalCompressor::POSTPONE, this);
connect(m_configCompressor, SIGNAL(timeout()), SLOT(setConfig()));
- //qDebug()<<"m_canvas:"<<m_canvas;
+ //dbgKrita<<"m_canvas:"<<m_canvas;
m_inputs.clear();
//this first creates and set the sliders.
//then setslidervisible is called to make them visible and connect them.
//This way they can also be disabled more easily.
//hsv sliders//
hsvH = new KisHSXColorSliderInput(this, 0, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsvH);
m_layout->addWidget(hsvH);
hsvH->setVisible(false);
hsvS = new KisHSXColorSliderInput(this, 1, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsvS);
m_layout->addWidget(hsvS);
hsvS->setVisible(false);
hsvV = new KisHSXColorSliderInput(this, 2, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsvV);
m_layout->addWidget(hsvV);
hsvV->setVisible(false);
//hsl sliders//
hslH = new KisHSXColorSliderInput(this, 3, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hslH);
m_layout->addWidget(hslH);
hslH->setVisible(false);
hslS = new KisHSXColorSliderInput(this, 4, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hslS);
m_layout->addWidget(hslS);
hslS->setVisible(false);
hslL = new KisHSXColorSliderInput(this, 5, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hslL);
m_layout->addWidget(hslL);
hslL->setVisible(false);
//hsi sliders//
hsiH = new KisHSXColorSliderInput(this, 6, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsiH);
m_layout->addWidget(hsiH);
hsiH->setVisible(false);
hsiS = new KisHSXColorSliderInput(this, 7, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsiS);
m_layout->addWidget(hsiS);
hsiS->setVisible(false);
hsiI = new KisHSXColorSliderInput(this, 8, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsiI);
m_layout->addWidget(hsiI);
hsiI->setVisible(false);
//hsy'sliders//
hsyH = new KisHSXColorSliderInput(this, 9, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsyH);
m_layout->addWidget(hsyH);
hsyH->setVisible(false);
hsyS = new KisHSXColorSliderInput(this, 10, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsyS);
m_layout->addWidget(hsyS);
hsyS->setVisible(false);
hsyY = new KisHSXColorSliderInput(this, 11, &m_color, m_displayRenderer, m_canvas);
m_inputs.append(hsyY);
m_layout->addWidget(hsyY);
hsyY->setVisible(false);
m_layout->addStretch(1);
setSlidersVisible(SlidersConfigArray);
}
KisColorSliderWidget::~KisColorSliderWidget()
{
//KConfigGroup cfg = KGlobal::config()->group("");
//cfg.writeEntry("SpecificColorSelector/ShowColorSpaceSelector", m_chkShowColorSpaceSelector->isChecked());
}
void KisColorSliderWidget::update()
{
if (m_updateAllowed) {
m_updateCompressor->start();
}
}
void KisColorSliderWidget::setColor(const KoColor& c)
{
m_updateAllowed = false;
m_color.fromKoColor(c);
emit(updated());
m_updateAllowed = true;
}
void KisColorSliderWidget::updateTimeout()
{
emit(colorChanged(m_color));
}
void KisColorSliderWidget::setSlidersVisible(QBitArray SlidersConfigArray)
{
- //qDebug()<<"check2";
+ //dbgKrita<<"check2";
QList<KisColorSliderInput*> visibleInputs;
if (SlidersConfigArray[0]==true) {
visibleInputs.append(hsvH);
hsvH->setVisible(true);
connect(hsvH, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsvH, SLOT(update()));
connect(hsvH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
connect(this, SIGNAL(hueUpdated(int)), hsvH, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsvH, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsvH, SLOT(toneUpdate(int, int)));
}
else {
hsvH->setVisible(false);
disconnect(hsvH, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsvH, SLOT(update()));
disconnect(hsvH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(hueUpdated(int)), hsvH, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsvH, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsvH, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[1]==true) {
visibleInputs.append(hsvS);
hsvS->setVisible(true);
connect(hsvS, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsvS, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsvS, SLOT(hueUpdate(int)));
connect(hsvS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(satUpdated(int, int)), hsvS, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsvS, SLOT(toneUpdate(int, int)));
}
else {
hsvS->setVisible(false);
disconnect(hsvS, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsvS, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsvS, SLOT(hueUpdate(int)));
disconnect(hsvS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsvS, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsvS, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[2]==true) {
visibleInputs.append(hsvV);
hsvV->setVisible(true);
connect(hsvV, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsvV, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsvV, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsvV, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsvV, SLOT(toneUpdate(int, int)));
connect(hsvV, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
else {
hsvV->setVisible(false);
disconnect(hsvV, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsvV, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsvV, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsvV, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsvV, SLOT(toneUpdate(int, int)));
disconnect(hsvV, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[3]==true) {
visibleInputs.append(hslH);
hslH->setVisible(true);
connect(hslH, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hslH, SLOT(update()));
connect(hslH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
connect(this, SIGNAL(hueUpdated(int)), hslH, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hslH, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hslH, SLOT(toneUpdate(int, int)));
}
else {
hslH->setVisible(false);
disconnect(hslH, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hslH, SLOT(update()));
disconnect(hslH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(hueUpdated(int)), hslH, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hslH, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hslH, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[4]==true) {
visibleInputs.append(hslS);
hslS->setVisible(true);
connect(hslS, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hslS, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hslS, SLOT(hueUpdate(int)));
connect(hslS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(satUpdated(int, int)), hslS, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hslS, SLOT(toneUpdate(int, int)));
}
else {
hslS->setVisible(false);
disconnect(hslS, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hslS, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hslS, SLOT(hueUpdate(int)));
disconnect(hslS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hslS, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hslS, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[5]==true) {
visibleInputs.append(hslL);
hslL->setVisible(true);
connect(hslL, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hslL, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hslL, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hslL, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hslL, SLOT(toneUpdate(int, int)));
connect(hslL, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
else {
hslL->setVisible(false);
disconnect(hslL, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hslL, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hslL, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hslL, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hslL, SLOT(toneUpdate(int, int)));
disconnect(hslL, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[6]==true) {
visibleInputs.append(hsiH);
hsiH->setVisible(true);
connect(hsiH, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsiH, SLOT(update()));
connect(hsiH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
connect(this, SIGNAL(hueUpdated(int)), hsiH, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsiH, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsiH, SLOT(toneUpdate(int, int)));
}
else {
hsiH->setVisible(false);
disconnect(hsiH, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsiH, SLOT(update()));
disconnect(hsiH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(hueUpdated(int)), hsiH, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsiH, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsiH, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[7]==true) {
visibleInputs.append(hsiS);
hsiS->setVisible(true);
connect(hsiS, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsiS, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsiS, SLOT(hueUpdate(int)));
connect(hsiS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(satUpdated(int, int)), hsiS, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsiS, SLOT(toneUpdate(int, int)));
}
else {
hsiS->setVisible(false);
disconnect(hsiS, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsiS, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsiS, SLOT(hueUpdate(int)));
disconnect(hsiS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsiS, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsiS, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[8]==true) {
visibleInputs.append(hsiI);
hsiI->setVisible(true);
connect(hsiI, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsiI, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsiI, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsiI, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsiI, SLOT(toneUpdate(int, int)));
connect(hsiI, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
else {
hsiI->setVisible(false);
disconnect(hsiI, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsiI, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsiI, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsiI, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsiI, SLOT(toneUpdate(int, int)));
disconnect(hsiI, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[9]==true) {
visibleInputs.append(hsyH);
hsyH->setVisible(true);
connect(hsyH, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsyH, SLOT(update()));
connect(hsyH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
connect(this, SIGNAL(hueUpdated(int)), hsyH, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsyH, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsyH, SLOT(toneUpdate(int, int)));
}
else {
hsyH->setVisible(false);
disconnect(hsyH, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsyH, SLOT(update()));
disconnect(hsyH, SIGNAL(hueUpdated(int)), this, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(hueUpdated(int)), hsyH, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsyH, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsyH, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[10]==true) {
visibleInputs.append(hsyS);
hsyS->setVisible(true);
connect(hsyS, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsyS, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsyS, SLOT(hueUpdate(int)));
connect(hsyS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(satUpdated(int, int)), hsyS, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsyS, SLOT(toneUpdate(int, int)));
}
else {
hsyS->setVisible(false);
disconnect(hsyS, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsyS, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsyS, SLOT(hueUpdate(int)));
disconnect(hsyS, SIGNAL(satUpdated(int, int)), this, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsyS, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsyS, SLOT(toneUpdate(int, int)));
}
if (SlidersConfigArray[11]==true) {
visibleInputs.append(hsyY);
hsyY->setVisible(true);
connect(hsyY, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), hsyY, SLOT(update()));
connect(this, SIGNAL(hueUpdated(int)), hsyY, SLOT(hueUpdate(int)));
connect(this, SIGNAL(satUpdated(int, int)), hsyY, SLOT(satUpdate(int, int)));
connect(this, SIGNAL(toneUpdated(int, int)), hsyY, SLOT(toneUpdate(int, int)));
connect(hsyY, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
else {
hsyY->setVisible(false);
disconnect(hsyY, SIGNAL(updated()), this, SLOT(update()));
disconnect(this, SIGNAL(updated()), hsyY, SLOT(update()));
disconnect(this, SIGNAL(hueUpdated(int)), hsyY, SLOT(hueUpdate(int)));
disconnect(this, SIGNAL(satUpdated(int, int)), hsyY, SLOT(satUpdate(int, int)));
disconnect(this, SIGNAL(toneUpdated(int, int)), hsyY, SLOT(toneUpdate(int, int)));
disconnect(hsyY, SIGNAL(toneUpdated(int, int)), this, SLOT(toneUpdate(int, int)));
}
QList<QLabel*> labels;
int labelWidth = 0;
Q_FOREACH (KisColorSliderInput* input, visibleInputs) {
Q_FOREACH (QLabel* label, input->findChildren<QLabel*>()) {
labels.append(label);
labelWidth = qMax(labelWidth, label->sizeHint().width());
}
}
Q_FOREACH (QLabel *label, labels) {
label->setMinimumWidth(labelWidth);
}
updateTimeout();
}
void KisColorSliderWidget::slotConfigChanged()
{
if (m_updateAllowed) {
m_configCompressor->start();
}
}
void KisColorSliderWidget::setConfig()
{
//QTimer::singleShot(1, this, SLOT(update()));//need to wait a bit before accessing the config.
QBitArray m_SlidersConfigArray(12);
- //qDebug()<<"check";
+ //dbgKrita<<"check";
KConfigGroup cfg = KGlobal::config()->group("hsxColorSlider");
m_SlidersConfigArray[0] =cfg.readEntry("hsvH", false);
m_SlidersConfigArray[1] =cfg.readEntry("hsvS", false);
m_SlidersConfigArray[2] =cfg.readEntry("hsvV", false);
m_SlidersConfigArray[3] =cfg.readEntry("hslH", true);
m_SlidersConfigArray[4] =cfg.readEntry("hslS", true);
m_SlidersConfigArray[5] =cfg.readEntry("hslL", true);
m_SlidersConfigArray[6] =cfg.readEntry("hsiH", false);
m_SlidersConfigArray[7] =cfg.readEntry("hsiS", false);
m_SlidersConfigArray[8] =cfg.readEntry("hsiI", false);
m_SlidersConfigArray[9] =cfg.readEntry("hsyH", false);
m_SlidersConfigArray[10]=cfg.readEntry("hsyS", false);
m_SlidersConfigArray[11]=cfg.readEntry("hsyY", false);
setSlidersVisible(m_SlidersConfigArray);
}
void KisColorSliderWidget::hueUpdate(int h)
{
emit(hueUpdated(h));
}
void KisColorSliderWidget::satUpdate(int s, int type)
{
emit(satUpdated(s, type));
}
void KisColorSliderWidget::toneUpdate(int l, int type)
{
emit(toneUpdated(l, type));
}
#include "moc_kis_color_slider_widget.cpp"
\ No newline at end of file
diff --git a/krita/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp b/krita/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp
index 98d3f0c6cb6..de3f525544f 100644
--- a/krita/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp
+++ b/krita/plugins/extensions/dockers/compositiondocker/compositiondocker_dock.cpp
@@ -1,291 +1,291 @@
/*
* Copyright (c) 2012 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "compositiondocker_dock.h"
#include <QGridLayout>
#include <QListView>
#include <QHeaderView>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QInputDialog>
#include <QThread>
#include <QAction>
#include <QDesktopServices>
#include <QMenu>
#include <klocale.h>
#include <kactioncollection.h>
#include <KoIcon.h>
#include <KoCanvasBase.h>
#include <KoFileDialog.h>
#include <KisPart.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_painter.h>
#include <kis_paint_layer.h>
#include <kis_action.h>
#include <kis_action_manager.h>
#include "compositionmodel.h"
CompositionDockerDock::CompositionDockerDock( ) : QDockWidget(i18n("Compositions")), m_canvas(0)
{
QWidget* widget = new QWidget(this);
setupUi(widget);
m_model = new CompositionModel(this);
compositionView->setModel(m_model);
compositionView->installEventFilter(this);
deleteButton->setIcon(themedIcon("edit-delete"));
saveButton->setIcon(themedIcon("list-add"));
exportButton->setIcon(themedIcon("document-export"));
deleteButton->setToolTip(i18n("Delete Composition"));
saveButton->setToolTip(i18n("New Composition"));
exportButton->setToolTip(i18n("Export Composition"));
setWidget(widget);
connect( compositionView, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(activated ( const QModelIndex & ) ) );
compositionView->setContextMenuPolicy(Qt::CustomContextMenu);
connect( compositionView, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(customContextMenuRequested(QPoint)));
connect( deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked()));
connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked()));
connect( exportButton, SIGNAL(clicked(bool)), this, SLOT(exportClicked()));
saveNameEdit->setPlaceholderText(i18n("Insert Name"));
updateAction = new KisAction(i18n("Update Composition"), this);
updateAction->setObjectName("update_composition");
connect(updateAction, SIGNAL(triggered()), this, SLOT(updateComposition()));
renameAction = new KisAction(i18n("Rename Composition..."), this);
renameAction->setObjectName("rename_composition");
connect(renameAction, SIGNAL(triggered()), this, SLOT(renameComposition()));
m_actions.append(renameAction);
}
CompositionDockerDock::~CompositionDockerDock()
{
}
void CompositionDockerDock::setCanvas(KoCanvasBase * canvas)
{
unsetCanvas();
setEnabled(canvas != 0);
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (m_canvas) {
foreach(KisAction *action, m_actions) {
m_canvas->viewManager()->actionManager()->addAction(action->objectName(), action);
}
updateModel();
}
}
void CompositionDockerDock::unsetCanvas()
{
setEnabled(false);
if (m_canvas) {
foreach(KisAction *action, m_actions) {
m_canvas->viewManager()->actionManager()->takeAction(action);
}
}
m_canvas = 0;
m_model->setCompositions(QList<KisLayerComposition*>());
}
void CompositionDockerDock::activated(const QModelIndex& index)
{
KisLayerComposition* composition = m_model->compositionFromIndex(index);
composition->apply();
}
void CompositionDockerDock::deleteClicked()
{
QModelIndex index = compositionView->currentIndex();
if(index.isValid()) {
KisLayerComposition* composition = m_model->compositionFromIndex(index);
m_canvas->viewManager()->image()->removeComposition(composition);
updateModel();
}
}
void CompositionDockerDock::saveClicked()
{
KisImageWSP image = m_canvas->viewManager()->image();
// format as 001, 002 ...
QString name = saveNameEdit->text();
if (name.isEmpty()) {
bool found = false;
int i = 1;
do {
name = QString("%1").arg(i, 3, 10, QChar('0'));
found = false;
foreach(KisLayerComposition* composition, m_canvas->viewManager()->image()->compositions()) {
if (composition->name() == name) {
found = true;
break;
}
}
i++;
} while(found && i < 1000);
}
KisLayerComposition* composition = new KisLayerComposition(image, name);
composition->store();
image->addComposition(composition);
saveNameEdit->clear();
updateModel();
compositionView->setCurrentIndex(m_model->index(image->compositions().count()-1, 0));
image->setModified();
}
void CompositionDockerDock::updateModel()
{
m_model->setCompositions(m_canvas->viewManager()->image()->compositions());
}
void CompositionDockerDock::exportClicked()
{
QString path;
KoFileDialog dialog(0, KoFileDialog::OpenDirectory, "krita/compositiondockerdock");
dialog.setCaption(i18n("Select a Directory"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
path = dialog.url();
if (path.isNull()) return;
if (!path.endsWith('/')) {
path.append('/');
}
KisImageWSP image = m_canvas->viewManager()->image();
QString filename = m_canvas->viewManager()->document()->localFilePath();
if (!filename.isEmpty()) {
QFileInfo info(filename);
path += info.baseName() + '_';
}
foreach(KisLayerComposition* composition, m_canvas->viewManager()->image()->compositions()) {
if (!composition->isExportEnabled()) {
continue;
}
composition->apply();
image->refreshGraph();
image->lock();
#if 0
image->rootLayer()->projection()->convertToQImage(0, 0, 0, image->width(), image->height()).save(path + composition->name() + ".png");
#else
QRect r = image->bounds();
KisDocument *d = KisPart::instance()->createDocument();
d->prepareForImport();
KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), image->colorSpace(), composition->name());
dst->setResolution(image->xRes(), image->yRes());
d->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", OPACITY_OPAQUE_U8);
KisPainter gc(paintLayer->paintDevice());
gc.bitBlt(QPoint(0, 0), image->rootLayer()->projection(), r);
dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
dst->refreshGraph();
d->setOutputMimeType("image/png");
d->setSaveInBatchMode(true);
d->exportDocument(KUrl(path + composition->name() + ".png"));
delete d;
#endif
image->unlock();
}
}
bool CompositionDockerDock::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::KeyPress ) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) {
// new index will be set after the method is called
QTimer::singleShot(0, this, SLOT(activateCurrentIndex()));
}
return false;
} else {
return QObject::eventFilter(obj, event);
}
}
void CompositionDockerDock::activateCurrentIndex()
{
QModelIndex index = compositionView->currentIndex();
if (index.isValid()) {
activated(index);
}
}
void CompositionDockerDock::customContextMenuRequested(QPoint pos)
{
QMenu menu;
menu.addAction(updateAction);
menu.addAction(renameAction);
menu.exec(compositionView->mapToGlobal(pos));
}
void CompositionDockerDock::updateComposition()
{
QModelIndex index = compositionView->currentIndex();
if (index.isValid()) {
KisLayerComposition* composition = m_model->compositionFromIndex(index);
composition->store();
m_canvas->image()->setModified();
}
}
void CompositionDockerDock::renameComposition()
{
- kDebug() << "rename";
+ dbgKrita << "rename";
QModelIndex index = compositionView->currentIndex();
if (index.isValid()) {
KisLayerComposition* composition = m_model->compositionFromIndex(index);
bool ok;
QString name = QInputDialog::getText(this, i18n("Rename Composition"),
i18n("New Name:"), QLineEdit::Normal,
composition->name(), &ok);
if (ok && !name.isEmpty()) {
composition->setName(name);
m_canvas->image()->setModified();
}
}
}
diff --git a/krita/plugins/extensions/dockers/imagedocker/image_strip_scene.cpp b/krita/plugins/extensions/dockers/imagedocker/image_strip_scene.cpp
index 141e7ae923e..0f7224e4694 100644
--- a/krita/plugins/extensions/dockers/imagedocker/image_strip_scene.cpp
+++ b/krita/plugins/extensions/dockers/imagedocker/image_strip_scene.cpp
@@ -1,199 +1,199 @@
/*
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "image_strip_scene.h"
#include <KoIcon.h>
#include <QApplication>
#include <QDir>
#include <QPainter>
#include <QHash>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>
#include <QMutexLocker>
-#include <QDebug>
+#include <kis_debug.h>
/////////////////////////////////////////////////////////////////////////////////////////////
// ------------- ImageLoader ---------------------------------------------------------- //
ImageLoader::ImageLoader(float size)
: m_size(size)
, m_run(true)
{
connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(stopExecution()));
}
void ImageLoader::run()
{
typedef QHash<ImageItem*,Data>::iterator Iterator;
for (Iterator data = m_data.begin(); data != m_data.end() && m_run; ++data) {
QImage img = QImage(data->path);
if (!img.isNull()) {
data->image = img.scaled(m_size, m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
- //qDebug() << "Loaded" << data->path;
+ //dbgKrita << "Loaded" << data->path;
data->isLoaded = true;
emit sigItemContentChanged(data.key());
}
}
void ImageLoader::stopExecution()
{
m_run = false;
}
/////////////////////////////////////////////////////////////////////////////////////////////
// ------------- ImageItem ------------------------------------------------------------ //
void ImageItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
if (m_loader->isImageLoaded(this)) {
QImage image = m_loader->getImage(this);
if (!image.isNull()) {
QPointF offset((m_size-image.width()) / 2.0, (m_size-image.height()) / 2.0);
painter->drawImage(offset, image);
}
else {
QIcon icon = themedIcon("edit-delete");
QRect rect = boundingRect().toRect();
QPixmap img = icon.pixmap(rect.size());
painter->drawPixmap(rect, img, img.rect());
}
}
else {
QIcon icon = themedIcon("folder-pictures");
QRect rect = boundingRect().toRect();
QPixmap img = icon.pixmap(rect.size());
painter->drawPixmap(rect, img, img.rect());
}
if (isSelected()) {
painter->setCompositionMode(QPainter::CompositionMode_HardLight);
painter->setOpacity(0.50);
painter->fillRect(boundingRect().toRect(), palette().color(QPalette::Active, QPalette::Highlight));
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
QPen pen(palette().color(QPalette::Active, QPalette::Highlight), 3);
painter->setPen(pen);
}
painter->drawRect(boundingRect());
}
QSizeF ImageItem::sizeHint(Qt::SizeHint /*which*/, const QSizeF& /*constraint*/) const
{
return QSizeF(m_size, m_size);
}
/////////////////////////////////////////////////////////////////////////////////////////////
// ------------- ImageStripScene ------------------------------------------------------ //
ImageStripScene::ImageStripScene():
m_imgSize(80)
, m_loader(0)
{
}
ImageStripScene::~ImageStripScene()
{
delete m_loader;
}
bool ImageStripScene::setCurrentDirectory(const QString& path)
{
m_path = path;
QMutexLocker locker(&m_mutex);
QDir directory(path);
QImageReader reader;
if (directory.exists()) {
clear();
if (m_loader) {
m_loader->disconnect(this);
m_loader->stopExecution();
if (!m_loader->wait(500)) {
m_loader->terminate();
m_loader->wait();
}
}
delete m_loader;
m_numItems = 0;
m_loader = new ImageLoader(m_imgSize);
connect(m_loader, SIGNAL(sigItemContentChanged(ImageItem*)), SLOT(slotItemContentChanged(ImageItem*)));
QStringList files = directory.entryList(QDir::Files);
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout();
for (QStringList::iterator name=files.begin(); name!=files.end(); ++name) {
QString path = directory.absoluteFilePath(*name);
QString fileExtension = QFileInfo(path).suffix();
if (!fileExtension.compare("DNG", Qt::CaseInsensitive)) {
- qWarning() << "WARNING: Qt is known to crash when trying to open a DNG file. Skip it";
+ warnKrita << "WARNING: Qt is known to crash when trying to open a DNG file. Skip it";
continue;
}
reader.setFileName(path);
if(reader.canRead()) {
ImageItem* item = new ImageItem(m_imgSize, path, m_loader);
m_loader->addPath(item, path);
layout->addItem(item);
++m_numItems;
}
}
QGraphicsWidget* widget = new QGraphicsWidget();
widget->setLayout(layout);
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
addItem(widget);
setSceneRect(widget->boundingRect());
m_loader->start(QThread::LowPriority);
return true;
}
return false;
}
void ImageStripScene::slotItemContentChanged(ImageItem* item)
{
QMutexLocker locker(&m_mutex);
item->update();
}
void ImageStripScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
{
ImageItem* item = static_cast<ImageItem*>(itemAt(event->scenePos()));
if (item)
emit sigImageActivated(item->path());
}
diff --git a/krita/plugins/extensions/dockers/lut/lutdocker_dock.cpp b/krita/plugins/extensions/dockers/lut/lutdocker_dock.cpp
index 1fd76f581ff..9cae2dcafe7 100644
--- a/krita/plugins/extensions/dockers/lut/lutdocker_dock.cpp
+++ b/krita/plugins/extensions/dockers/lut/lutdocker_dock.cpp
@@ -1,565 +1,565 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "lutdocker_dock.h"
#include <sstream>
#include <QLayout>
#include <QLabel>
#include <QPixmap>
#include <QPainter>
#include <QImage>
#include <QFormLayout>
#include <QCheckBox>
#include <QApplication>
#include <QDesktopWidget>
#include <QToolButton>
#include <klocale.h>
#include <KisMainWindow.h>
#include <KoFileDialog.h>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceFactory.h>
#include <KoColorProfile.h>
#include <KoColorModelStandardIds.h>
#include "KoIcon.h"
#include <KisViewManager.h>
#include <KisDocument.h>
#include <kis_config.h>
#include <kis_canvas2.h>
#include <kis_canvas_resource_provider.h>
#include <kis_config_notifier.h>
#include <widgets/kis_double_widget.h>
#include <kis_image.h>
#include "widgets/squeezedcombobox.h"
#include "kis_signals_blocker.h"
#include "krita_utils.h"
#include "ocio_display_filter.h"
#include "black_white_point_chooser.h"
OCIO::ConstConfigRcPtr defaultRawProfile()
{
/**
* Copied from OCIO, just a noop profile
*/
const char * INTERNAL_RAW_PROFILE =
"ocio_profile_version: 1\n"
"strictparsing: false\n"
"roles:\n"
" default: raw\n"
"displays:\n"
" sRGB:\n"
" - !<View> {name: Raw, colorspace: raw}\n"
"colorspaces:\n"
" - !<ColorSpace>\n"
" name: raw\n"
" family: raw\n"
" equalitygroup:\n"
" bitdepth: 32f\n"
" isdata: true\n"
" allocation: uniform\n"
" description: 'A raw color space. Conversions to and from this space are no-ops.'\n";
std::istringstream istream;
istream.str(INTERNAL_RAW_PROFILE);
return OCIO::Config::CreateFromStream(istream);
}
LutDockerDock::LutDockerDock()
: QDockWidget(i18n("LUT Management"))
, m_canvas(0)
, m_draggingSlider(false)
{
m_exposureCompressor.reset(new KisSignalCompressorWithParam<qreal>(40, boost::bind(&LutDockerDock::setCurrentExposureImpl, this, _1)));
m_gammaCompressor.reset(new KisSignalCompressorWithParam<qreal>(40, boost::bind(&LutDockerDock::setCurrentGammaImpl, this, _1)));
m_page = new QWidget(this);
setupUi(m_page);
setWidget(m_page);
KisConfig cfg;
m_chkUseOcio->setChecked(cfg.useOcio());
connect(m_chkUseOcio, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings()));
connect(m_colorManagement, SIGNAL(currentIndexChanged(int)), SLOT(slotColorManagementModeChanged()));
m_txtConfigurationPath->setText(cfg.ocioConfigurationPath());
m_bnSelectConfigurationFile->setToolTip(i18n("Select custom configuration file."));
connect(m_bnSelectConfigurationFile,SIGNAL(clicked()), SLOT(selectOcioConfiguration()));
m_txtLut->setText(cfg.ocioLutPath());
m_bnSelectLut->setToolTip(i18n("Select LUT file"));
connect(m_bnSelectLut, SIGNAL(clicked()), SLOT(selectLut()));
connect(m_bnClearLut, SIGNAL(clicked()), SLOT(clearLut()));
// See http://groups.google.com/group/ocio-dev/browse_thread/thread/ec95c5f54a74af65 -- maybe need to be reinstated
// when people ask for it.
m_lblLut->hide();
m_txtLut->hide();
m_bnSelectLut->hide();
m_bnClearLut->hide();
connect(m_cmbDisplayDevice, SIGNAL(currentIndexChanged(int)), SLOT(refillViewCombobox()));
m_exposureDoubleWidget->setToolTip(i18n("Select the exposure (stops) for HDR images."));
m_exposureDoubleWidget->setRange(-10, 10);
m_exposureDoubleWidget->setPrecision(1);
m_exposureDoubleWidget->setValue(0.0);
m_exposureDoubleWidget->setSingleStep(0.25);
m_exposureDoubleWidget->setPageStep(1);
connect(m_exposureDoubleWidget, SIGNAL(valueChanged(double)), SLOT(exposureValueChanged(double)));
connect(m_exposureDoubleWidget, SIGNAL(sliderPressed()), SLOT(exposureSliderPressed()));
connect(m_exposureDoubleWidget, SIGNAL(sliderReleased()), SLOT(exposureSliderReleased()));
// Gamma needs to be exponential (gamma *= 1.1f, gamma /= 1.1f as steps)
m_gammaDoubleWidget->setToolTip(i18n("Select the amount of gamma modification for display. This does not affect the pixels of your image."));
m_gammaDoubleWidget->setRange(0.1, 5);
m_gammaDoubleWidget->setPrecision(2);
m_gammaDoubleWidget->setValue(1.0);
m_gammaDoubleWidget->setSingleStep(0.1);
m_gammaDoubleWidget->setPageStep(1);
connect(m_gammaDoubleWidget, SIGNAL(valueChanged(double)), SLOT(gammaValueChanged(double)));
connect(m_gammaDoubleWidget, SIGNAL(sliderPressed()), SLOT(gammaSliderPressed()));
connect(m_gammaDoubleWidget, SIGNAL(sliderReleased()), SLOT(gammaSliderReleased()));
m_bwPointChooser = new BlackWhitePointChooser(this);
connect(m_bwPointChooser, SIGNAL(sigBlackPointChanged(qreal)), SLOT(updateDisplaySettings()));
connect(m_bwPointChooser, SIGNAL(sigWhitePointChanged(qreal)), SLOT(updateDisplaySettings()));
connect(m_btnConvertCurrentColor, SIGNAL(toggled(bool)), SLOT(updateDisplaySettings()));
connect(m_btmShowBWConfiguration, SIGNAL(clicked()), SLOT(slotShowBWConfiguration()));
slotUpdateIcons();
connect(m_cmbInputColorSpace, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings()));
connect(m_cmbDisplayDevice, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings()));
connect(m_cmbView, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings()));
connect(m_cmbComponents, SIGNAL(currentIndexChanged(int)), SLOT(updateDisplaySettings()));
m_draggingSlider = false;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetOcioConfiguration()));
m_displayFilter = 0;
resetOcioConfiguration();
}
LutDockerDock::~LutDockerDock()
{
}
void LutDockerDock::setCanvas(KoCanvasBase* _canvas)
{
if (m_canvas) {
m_canvas->disconnect(this);
m_displayFilter = 0;
}
setEnabled(_canvas != 0);
if (KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(_canvas)) {
m_canvas = canvas;
if (m_canvas) {
if (!m_canvas->displayFilter()) {
m_displayFilter = new OcioDisplayFilter(this);
resetOcioConfiguration();
updateDisplaySettings();
}
else {
m_displayFilter = dynamic_cast<OcioDisplayFilter*>(m_canvas->displayFilter());
Q_ASSERT(m_displayFilter);
m_ocioConfig = m_displayFilter->config;
KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget);
m_exposureDoubleWidget->setValue(m_displayFilter->exposure);
KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget);
m_gammaDoubleWidget->setValue(m_displayFilter->gamma);
KisSignalsBlocker componentsBlocker(m_cmbComponents);
m_cmbComponents->setCurrentIndex((int)m_displayFilter->swizzle);
KisSignalsBlocker bwBlocker(m_bwPointChooser);
m_bwPointChooser->setBlackPoint(m_displayFilter->blackPoint);
m_bwPointChooser->setWhitePoint(m_displayFilter->whitePoint);
}
connect(m_canvas->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged()), Qt::UniqueConnection);
connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
}
}
}
void LutDockerDock::slotUpdateIcons()
{
m_btnConvertCurrentColor->setIcon(themedIcon("krita_tool_freehand"));
m_btmShowBWConfiguration->setIcon(themedIcon("properties"));
}
void LutDockerDock::slotShowBWConfiguration()
{
m_bwPointChooser->showPopup(m_btmShowBWConfiguration->mapToGlobal(QPoint()));
}
bool LutDockerDock::canChangeExposureAndGamma() const
{
return m_chkUseOcio->isChecked() && m_ocioConfig;
}
qreal LutDockerDock::currentExposure() const
{
if (!m_displayFilter) return 0.0;
return canChangeExposureAndGamma() ? m_displayFilter->exposure : 0.0;
}
void LutDockerDock::setCurrentExposure(qreal value)
{
if (!canChangeExposureAndGamma()) return;
m_exposureCompressor->start(value);
}
qreal LutDockerDock::currentGamma() const
{
if (!m_displayFilter) return 1.0;
return canChangeExposureAndGamma() ? m_displayFilter->gamma : 1.0;
}
void LutDockerDock::setCurrentGamma(qreal value)
{
if (!canChangeExposureAndGamma()) return;
m_gammaCompressor->start(value);
}
void LutDockerDock::setCurrentExposureImpl(qreal value)
{
m_exposureDoubleWidget->setValue(value);
if (!m_canvas) return;
m_canvas->viewManager()->showFloatingMessage(
i18nc("floating message about exposure", "Exposure: %1",
KritaUtils::prettyFormatReal(m_exposureDoubleWidget->value())),
QIcon(), 500, KisFloatingMessage::Low);
}
void LutDockerDock::setCurrentGammaImpl(qreal value)
{
m_gammaDoubleWidget->setValue(value);
if (!m_canvas) return;
m_canvas->viewManager()->showFloatingMessage(
i18nc("floating message about gamma", "Gamma: %1",
KritaUtils::prettyFormatReal(m_gammaDoubleWidget->value())),
QIcon(), 500, KisFloatingMessage::Low);
}
void LutDockerDock::slotImageColorSpaceChanged()
{
enableControls();
writeControls();
resetOcioConfiguration();
}
void LutDockerDock::exposureValueChanged(double exposure)
{
if (m_canvas && !m_draggingSlider) {
m_canvas->viewManager()->resourceProvider()->setHDRExposure(exposure);
updateDisplaySettings();
}
}
void LutDockerDock::exposureSliderPressed()
{
m_draggingSlider = true;
}
void LutDockerDock::exposureSliderReleased()
{
m_draggingSlider = false;
exposureValueChanged(m_exposureDoubleWidget->value());
}
void LutDockerDock::gammaValueChanged(double gamma)
{
if (m_canvas && !m_draggingSlider) {
m_canvas->viewManager()->resourceProvider()->setHDRGamma(gamma);
updateDisplaySettings();
}
}
void LutDockerDock::gammaSliderPressed()
{
m_draggingSlider = true;
}
void LutDockerDock::gammaSliderReleased()
{
m_draggingSlider = false;
gammaValueChanged(m_gammaDoubleWidget->value());
}
void LutDockerDock::enableControls()
{
bool canDoExternalColorCorrection = false;
if (m_canvas) {
KisImageSP image = m_canvas->viewManager()->image();
canDoExternalColorCorrection =
image->colorSpace()->colorModelId() == RGBAColorModelID;
}
if (!canDoExternalColorCorrection) {
KisSignalsBlocker colorManagementBlocker(m_colorManagement);
Q_UNUSED(colorManagementBlocker);
m_colorManagement->setCurrentIndex((int) KisConfig::INTERNAL);
}
bool ocioEnabled = m_chkUseOcio->isChecked();
m_colorManagement->setEnabled(ocioEnabled && canDoExternalColorCorrection);
bool externalColorManagementEnabled =
m_colorManagement->currentIndex() != (int)KisConfig::INTERNAL;
m_lblInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled);
m_cmbInputColorSpace->setEnabled(ocioEnabled && externalColorManagementEnabled);
m_lblDisplayDevice->setEnabled(ocioEnabled && externalColorManagementEnabled);
m_cmbDisplayDevice->setEnabled(ocioEnabled && externalColorManagementEnabled);
m_lblView->setEnabled(ocioEnabled && externalColorManagementEnabled);
m_cmbView->setEnabled(ocioEnabled && externalColorManagementEnabled);
bool enableConfigPath = m_colorManagement->currentIndex() == (int) KisConfig::OCIO_CONFIG;
lblConfig->setEnabled(ocioEnabled && enableConfigPath);
m_txtConfigurationPath->setEnabled(ocioEnabled && enableConfigPath);
m_bnSelectConfigurationFile->setEnabled(ocioEnabled && enableConfigPath);
}
void LutDockerDock::updateDisplaySettings()
{
if (!m_canvas || !m_canvas->viewManager() || !m_canvas->viewManager()->image()) {
return;
}
enableControls();
writeControls();
if (m_chkUseOcio->isChecked() && m_ocioConfig) {
m_displayFilter->config = m_ocioConfig;
m_displayFilter->inputColorSpaceName = m_ocioConfig->getColorSpaceNameByIndex(m_cmbInputColorSpace->currentIndex());
m_displayFilter->displayDevice = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex());
m_displayFilter->view = m_ocioConfig->getView(m_displayFilter->displayDevice, m_cmbView->currentIndex());
m_displayFilter->gamma = m_gammaDoubleWidget->value();
m_displayFilter->exposure = m_exposureDoubleWidget->value();
m_displayFilter->swizzle = (OCIO_CHANNEL_SWIZZLE)m_cmbComponents->currentIndex();
m_displayFilter->blackPoint = m_bwPointChooser->blackPoint();
m_displayFilter->whitePoint = m_bwPointChooser->whitePoint();
m_displayFilter->forceInternalColorManagement =
m_colorManagement->currentIndex() == (int)KisConfig::INTERNAL;
m_displayFilter->setLockCurrentColorVisualRepresentation(m_btnConvertCurrentColor->isChecked());
m_displayFilter->updateProcessor();
m_canvas->setDisplayFilter(m_displayFilter);
}
else {
m_canvas->setDisplayFilter(0);
}
m_canvas->updateCanvas();
}
void LutDockerDock::writeControls()
{
KisConfig cfg;
cfg.setUseOcio(m_chkUseOcio->isChecked());
cfg.setOcioColorManagementMode((KisConfig::OcioColorManagementMode) m_colorManagement->currentIndex());
cfg.setOcioLockColorVisualRepresentation(m_btnConvertCurrentColor->isChecked());
}
void LutDockerDock::slotColorManagementModeChanged()
{
enableControls();
writeControls();
resetOcioConfiguration();
}
void LutDockerDock::selectOcioConfiguration()
{
QString filename = m_txtConfigurationPath->text();
KoFileDialog dialog(this, KoFileDialog::OpenFile, "krita/lutdocker");
dialog.setCaption(i18n("Select OpenColorIO Configuration"));
dialog.setDefaultDir(QDir::cleanPath(filename));
dialog.setNameFilter(i18n("OpenColorIO configuration (*.ocio)"));
filename = dialog.url();
QFile f(filename);
if (f.exists()) {
m_txtConfigurationPath->setText(filename);
KisConfig cfg;
cfg.setOcioConfigurationPath(filename);
writeControls();
resetOcioConfiguration();
}
}
void LutDockerDock::resetOcioConfiguration()
{
m_ocioConfig.reset();
KisConfig cfg;
try {
if (cfg.ocioColorManagementMode() == KisConfig::INTERNAL) {
m_ocioConfig = defaultRawProfile();
} else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_ENVIRONMENT) {
m_ocioConfig = OCIO::Config::CreateFromEnv();
}
else if (cfg.ocioColorManagementMode() == KisConfig::OCIO_CONFIG) {
QString configFile = cfg.ocioConfigurationPath();
if (QFile::exists(configFile)) {
m_ocioConfig = OCIO::Config::CreateFromFile(configFile.toUtf8());
} else {
m_ocioConfig = defaultRawProfile();
}
}
if (m_ocioConfig) {
OCIO::SetCurrentConfig(m_ocioConfig);
}
}
catch (OCIO::Exception &exception) {
- kWarning() << "OpenColorIO Error:" << exception.what() << "Cannot create the LUT docker";
+ dbgKrita << "OpenColorIO Error:" << exception.what() << "Cannot create the LUT docker";
}
refillControls();
}
void LutDockerDock::refillControls()
{
if (!m_canvas) return;
KIS_ASSERT_RECOVER_RETURN(m_ocioConfig);
{ // Color Management Mode
KisConfig cfg;
KisSignalsBlocker modeBlocker(m_colorManagement);
m_colorManagement->setCurrentIndex((int) cfg.ocioColorManagementMode());
}
{ // Exposure
KisSignalsBlocker exposureBlocker(m_exposureDoubleWidget);
m_exposureDoubleWidget->setValue(m_canvas->viewManager()->resourceProvider()->HDRExposure());
}
{ // Gamma
KisSignalsBlocker gammaBlocker(m_gammaDoubleWidget);
m_gammaDoubleWidget->setValue(m_canvas->viewManager()->resourceProvider()->HDRGamma());
}
{ // Components
const KoColorSpace *cs = m_canvas->viewManager()->image()->colorSpace();
KisSignalsBlocker componentsBlocker(m_cmbComponents);
m_cmbComponents->clear();
m_cmbComponents->addSqueezedItem(i18n("Luminance"));
m_cmbComponents->addSqueezedItem(i18n("All Channels"));
foreach(KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(cs->channels())) {
m_cmbComponents->addSqueezedItem(channel->name());
}
m_cmbComponents->setCurrentIndex(1); // All Channels...
}
{ // Input Color Space
KisSignalsBlocker inputCSBlocker(m_cmbInputColorSpace);
m_cmbInputColorSpace->clear();
int numOcioColorSpaces = m_ocioConfig->getNumColorSpaces();
for(int i = 0; i < numOcioColorSpaces; ++i) {
const char *cs = m_ocioConfig->getColorSpaceNameByIndex(i);
OCIO::ConstColorSpaceRcPtr colorSpace = m_ocioConfig->getColorSpace(cs);
m_cmbInputColorSpace->addSqueezedItem(QString::fromUtf8(colorSpace->getName()));
}
}
{ // Display Device
KisSignalsBlocker displayDeviceLocker(m_cmbDisplayDevice);
m_cmbDisplayDevice->clear();
int numDisplays = m_ocioConfig->getNumDisplays();
for (int i = 0; i < numDisplays; ++i) {
m_cmbDisplayDevice->addSqueezedItem(QString::fromUtf8(m_ocioConfig->getDisplay(i)));
}
}
{ // Lock Current Color
KisSignalsBlocker locker(m_btnConvertCurrentColor);
KisConfig cfg;
m_btnConvertCurrentColor->setChecked(cfg.ocioLockColorVisualRepresentation());
}
refillViewCombobox();
updateDisplaySettings();
}
void LutDockerDock::refillViewCombobox()
{
KisSignalsBlocker viewComboLocker(m_cmbView);
m_cmbView->clear();
if (!m_canvas || !m_ocioConfig) return;
const char *display = m_ocioConfig->getDisplay(m_cmbDisplayDevice->currentIndex());
int numViews = m_ocioConfig->getNumViews(display);
for (int j = 0; j < numViews; ++j) {
m_cmbView->addSqueezedItem(QString::fromUtf8(m_ocioConfig->getView(display, j)));
}
}
void LutDockerDock::selectLut()
{
QString filename = m_txtLut->text();
KoFileDialog dialog(this, KoFileDialog::OpenFile, "krita/lutdocker");
dialog.setCaption(i18n("Select LUT file"));
dialog.setDefaultDir(QDir::cleanPath(filename));
dialog.setNameFilter("*.*");
filename = dialog.url();
QFile f(filename);
if (f.exists() && filename != m_txtLut->text()) {
m_txtLut->setText(filename);
KisConfig cfg;
cfg.setOcioLutPath(filename);
updateDisplaySettings();
}
}
void LutDockerDock::clearLut()
{
m_txtLut->clear();
updateDisplaySettings();
}
diff --git a/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp b/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp
index 10d16f5839e..714adac7ad5 100644
--- a/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp
+++ b/krita/plugins/extensions/dockers/lut/ocio_display_filter.cpp
@@ -1,314 +1,314 @@
/*
* Copyright (c) 2012 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ocio_display_filter.h"
#include <math.h>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
#include <kis_config.h>
#ifdef HAVE_OPENGL
#include <opengl/kis_opengl.h>
#include <QOpenGLContext>
#endif
static const int LUT3D_EDGE_SIZE = 32;
OcioDisplayFilter::OcioDisplayFilter(KisExposureGammaCorrectionInterface *interface, QObject *parent)
: KisDisplayFilter(parent)
, inputColorSpaceName(0)
, displayDevice(0)
, view(0)
, swizzle(RGBA)
, m_interface(interface)
#ifdef HAVE_OPENGL
, m_lut3dTexID(0)
#endif
{
}
OcioDisplayFilter::~OcioDisplayFilter()
{
}
KisExposureGammaCorrectionInterface* OcioDisplayFilter::correctionInterface() const
{
return m_interface;
}
void OcioDisplayFilter::filter(quint8 *pixels, quint32 numPixels)
{
// processes that data _in_ place
if (m_processor) {
OCIO::PackedImageDesc img(reinterpret_cast<float*>(pixels), numPixels, 1, 4);
m_processor->apply(img);
}
}
void OcioDisplayFilter::approximateInverseTransformation(quint8 *pixels, quint32 numPixels)
{
// processes that data _in_ place
if (m_revereseApproximationProcessor) {
OCIO::PackedImageDesc img(reinterpret_cast<float*>(pixels), numPixels, 1, 4);
m_revereseApproximationProcessor->apply(img);
}
}
void OcioDisplayFilter::approximateForwardTransformation(quint8 *pixels, quint32 numPixels)
{
// processes that data _in_ place
if (m_forwardApproximationProcessor) {
OCIO::PackedImageDesc img(reinterpret_cast<float*>(pixels), numPixels, 1, 4);
m_forwardApproximationProcessor->apply(img);
}
}
bool OcioDisplayFilter::useInternalColorManagement() const
{
return forceInternalColorManagement;
}
bool OcioDisplayFilter::lockCurrentColorVisualRepresentation() const
{
return m_lockCurrentColorVisualRepresentation;
}
void OcioDisplayFilter::setLockCurrentColorVisualRepresentation(bool value)
{
m_lockCurrentColorVisualRepresentation = value;
}
#ifdef HAVE_OPENGL
QString OcioDisplayFilter::program() const
{
return m_program;
}
GLuint OcioDisplayFilter::lutTexture() const
{
return m_lut3dTexID;
}
#endif
void OcioDisplayFilter::updateProcessor()
{
if (!config) {
return;
}
if (!displayDevice) {
displayDevice = config->getDefaultDisplay();
}
if (!view) {
view = config->getDefaultView(displayDevice);
}
if (!inputColorSpaceName) {
inputColorSpaceName = config->getColorSpaceNameByIndex(0);
}
OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create();
transform->setInputColorSpaceName(inputColorSpaceName);
transform->setDisplay(displayDevice);
transform->setView(view);
OCIO::GroupTransformRcPtr approximateTransform = OCIO::GroupTransform::Create();
// fstop exposure control -- not sure how that translates to our exposure
{
float exposureGain = powf(2.0f, exposure);
const qreal minRange = 0.001;
if (qAbs(blackPoint - whitePoint) < minRange) {
whitePoint = blackPoint + minRange;
}
const float oldMin[] = { blackPoint, blackPoint, blackPoint, 0.0f };
const float oldMax[] = { whitePoint, whitePoint, whitePoint, 1.0f };
const float newMin[] = { 0.0f, 0.0f, 0.0f, 0.0f };
const float newMax[] = { exposureGain, exposureGain, exposureGain, 1.0f };
float m44[16];
float offset4[4];
OCIO::MatrixTransform::Fit(m44, offset4, oldMin, oldMax, newMin, newMax);
OCIO::MatrixTransformRcPtr mtx = OCIO::MatrixTransform::Create();
mtx->setValue(m44, offset4);
transform->setLinearCC(mtx);
// approximation (no color correction);
approximateTransform->push_back(mtx);
}
// channel swizzle
{
int channelHot[4];
switch (swizzle) {
case LUMINANCE:
channelHot[0] = 1;
channelHot[1] = 1;
channelHot[2] = 1;
channelHot[3] = 0;
break;
case RGBA:
channelHot[0] = 1;
channelHot[1] = 1;
channelHot[2] = 1;
channelHot[3] = 1;
break;
case R:
channelHot[0] = 1;
channelHot[1] = 0;
channelHot[2] = 0;
channelHot[3] = 0;
break;
case G:
channelHot[0] = 0;
channelHot[1] = 1;
channelHot[2] = 0;
channelHot[3] = 0;
break;
case B:
channelHot[0] = 0;
channelHot[1] = 0;
channelHot[2] = 1;
channelHot[3] = 0;
break;
case A:
channelHot[0] = 0;
channelHot[1] = 0;
channelHot[2] = 0;
channelHot[3] = 1;
default:
;
}
float lumacoef[3];
config->getDefaultLumaCoefs(lumacoef);
float m44[16];
float offset[4];
OCIO::MatrixTransform::View(m44, offset, channelHot, lumacoef);
OCIO::MatrixTransformRcPtr swizzle = OCIO::MatrixTransform::Create();
swizzle->setValue(m44, offset);
transform->setChannelView(swizzle);
}
// Post-display transform gamma
{
float exponent = 1.0f/std::max(1e-6f, static_cast<float>(gamma));
const float exponent4f[] = { exponent, exponent, exponent, exponent };
OCIO::ExponentTransformRcPtr expTransform = OCIO::ExponentTransform::Create();
expTransform->setValue(exponent4f);
transform->setDisplayCC(expTransform);
// approximation (no color correction);
approximateTransform->push_back(expTransform);
}
m_processor = config->getProcessor(transform);
m_forwardApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_FORWARD);
try {
m_revereseApproximationProcessor = config->getProcessor(approximateTransform, OCIO::TRANSFORM_DIR_INVERSE);
} catch (...) {
- qWarning() << "OCIO inverted matrix does not exist!";
+ warnKrita << "OCIO inverted matrix does not exist!";
//m_revereseApproximationProcessor;
}
#ifdef HAVE_OPENGL
// check whether we are allowed to use shaders -- though that should
// work for everyone these days
KisConfig cfg;
if (!cfg.useOpenGL()) return;
const int lut3DEdgeSize = cfg.ocioLutEdgeSize();
if (m_lut3d.size() == 0) {
- //qDebug() << "generating lut";
+ //dbgKrita << "generating lut";
glGenTextures(1, &m_lut3dTexID);
int num3Dentries = 3 * lut3DEdgeSize * lut3DEdgeSize * lut3DEdgeSize;
m_lut3d.fill(0.0, num3Dentries);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_3D, m_lut3dTexID);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB,
lut3DEdgeSize, lut3DEdgeSize, lut3DEdgeSize,
0, GL_RGB, GL_FLOAT, &m_lut3d.constData()[0]);
}
// Step 1: Create a GPU Shader Description
OCIO::GpuShaderDesc shaderDesc;
if (KisOpenGL::supportsGLSL13()) {
shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3);
} else {
shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0);
}
shaderDesc.setFunctionName("OCIODisplay");
shaderDesc.setLut3DEdgeLen(lut3DEdgeSize);
// Step 2: Compute the 3D LUT
QString lut3dCacheID = QString::fromLatin1(m_processor->getGpuLut3DCacheID(shaderDesc));
if(lut3dCacheID != m_lut3dcacheid)
{
- //qDebug() << "Computing 3DLut " << m_lut3dcacheid;
+ //dbgKrita << "Computing 3DLut " << m_lut3dcacheid;
m_lut3dcacheid = lut3dCacheID;
m_processor->getGpuLut3D(&m_lut3d[0], shaderDesc);
glBindTexture(GL_TEXTURE_3D, m_lut3dTexID);
glTexSubImage3D(GL_TEXTURE_3D, 0,
0, 0, 0,
lut3DEdgeSize, lut3DEdgeSize, lut3DEdgeSize,
GL_RGB, GL_FLOAT, &m_lut3d[0]);
}
// Step 3: Generate the shader text
QString shaderCacheID = QString::fromLatin1(m_processor->getGpuShaderTextCacheID(shaderDesc));
if (m_program.isEmpty() || shaderCacheID != m_shadercacheid) {
- //qDebug() << "Computing Shader " << m_shadercacheid;
+ //dbgKrita << "Computing Shader " << m_shadercacheid;
m_shadercacheid = shaderCacheID;
std::ostringstream os;
os << m_processor->getGpuShaderText(shaderDesc) << "\n";
m_program = QString::fromLatin1(os.str().c_str());
}
#endif
}
diff --git a/krita/plugins/extensions/dockers/lut/tests/kis_ocio_display_filter_test.cpp b/krita/plugins/extensions/dockers/lut/tests/kis_ocio_display_filter_test.cpp
index 27f59e9881f..963dc54f8c0 100644
--- a/krita/plugins/extensions/dockers/lut/tests/kis_ocio_display_filter_test.cpp
+++ b/krita/plugins/extensions/dockers/lut/tests/kis_ocio_display_filter_test.cpp
@@ -1,119 +1,119 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_ocio_display_filter_test.h"
#include <qtest_kde.h>
#include <QFile>
#include "KoColorModelStandardIds.h"
#include "../../../../../sdk/tests/stroke_testing_utils.h"
#include "../../../../../sdk/tests/testutil.h"
#include "kis_exposure_gamma_correction_interface.h"
#include "../ocio_display_filter.h"
#include "kis_display_color_converter.h"
#include "kis_canvas_resource_provider.h"
#include <KoChannelInfo.h>
void KisOcioDisplayFilterTest::test()
{
KisExposureGammaCorrectionInterface *egInterface =
new KisDumbExposureGammaCorrectionInterface();
OcioDisplayFilter filter(egInterface);
QString configFile = TestUtil::fetchDataFileLazy("./psyfiTestingConfig-master/config.ocio");
- qDebug() << ppVar(configFile);
+ dbgKrita << ppVar(configFile);
Q_ASSERT(QFile::exists(configFile));
OCIO::ConstConfigRcPtr ocioConfig =
OCIO::Config::CreateFromFile(configFile.toUtf8());
filter.config = ocioConfig;
filter.inputColorSpaceName = ocioConfig->getColorSpaceNameByIndex(0);
filter.displayDevice = ocioConfig->getDisplay(1);
filter.view = ocioConfig->getView(filter.displayDevice, 0);
filter.gamma = 1.0;
filter.exposure = 0.0;
filter.swizzle = RGBA;
filter.blackPoint = 0.0;
filter.whitePoint = 1.0;
filter.forceInternalColorManagement = false;
filter.setLockCurrentColorVisualRepresentation(false);
filter.updateProcessor();
- qDebug() << ppVar(filter.inputColorSpaceName);
- qDebug() << ppVar(filter.displayDevice);
- qDebug() << ppVar(filter.view);
- qDebug() << ppVar(filter.gamma);
- qDebug() << ppVar(filter.exposure);
+ dbgKrita << ppVar(filter.inputColorSpaceName);
+ dbgKrita << ppVar(filter.displayDevice);
+ dbgKrita << ppVar(filter.view);
+ dbgKrita << ppVar(filter.gamma);
+ dbgKrita << ppVar(filter.exposure);
const KoColorSpace *paintingCS =
KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), 0);
KisImageSP image = utils::createImage(0, QSize(100, 100));
image->convertImageColorSpace(paintingCS, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
image->waitForDone();
-qDebug() << ppVar(paintingCS) << ppVar(image->root()->firstChild()->colorSpace());
+dbgKrita << ppVar(paintingCS) << ppVar(image->root()->firstChild()->colorSpace());
KoCanvasResourceManager *resourceManager =
utils::createResourceManager(image,
image->root(), "");
KisDisplayColorConverter converter(resourceManager, 0);
- qDebug() << ppVar(image->root()->firstChild());
+ dbgKrita << ppVar(image->root()->firstChild());
QVariant v;
v.setValue(KisNodeWSP(image->root()->firstChild()));
resourceManager->setResource(KisCanvasResourceProvider::CurrentKritaNode, v);
converter.setDisplayFilter(&filter);
- qDebug() << ppVar(converter.paintingColorSpace());
+ dbgKrita << ppVar(converter.paintingColorSpace());
{
QColor refColor(255, 128, 0);
KoColor realColor = converter.approximateFromRenderedQColor(refColor);
QColor roundTripColor = converter.toQColor(realColor);
- qDebug() << ppVar(refColor);
- qDebug() << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
- qDebug() << ppVar(roundTripColor);
+ dbgKrita << ppVar(refColor);
+ dbgKrita << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
+ dbgKrita << ppVar(roundTripColor);
}
{
KoColor realColor(Qt::red, paintingCS);
QColor roundTripColor = converter.toQColor(realColor);
- qDebug() << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
- qDebug() << ppVar(roundTripColor);
+ dbgKrita << ppVar(realColor.colorSpace()) << ppVar(KoColor::toQString(realColor));
+ dbgKrita << ppVar(roundTripColor);
}
}
QTEST_KDEMAIN(KisOcioDisplayFilterTest, GUI)
diff --git a/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.cpp b/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.cpp
index 691b7e7c6d0..24caef88f66 100644
--- a/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.cpp
+++ b/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.cpp
@@ -1,328 +1,332 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "palettedocker_dock.h"
#include <QPainter>
#include <QGridLayout>
#include <QTableView>
#include <QHeaderView>
#include <QWheelEvent>
#include <klocale.h>
#include <kcolordialog.h>
#include <KoIcon.h>
#include <KoResourceServerProvider.h>
#include <KoColorSpaceRegistry.h>
#include <kis_layer.h>
#include <kis_node_manager.h>
#include <kis_config.h>
#include <kis_workspace_resource.h>
#include <kis_canvas_resource_provider.h>
#include <KisMainWindow.h>
#include <kis_canvas_resource_provider.h>
#include <KisViewManager.h>
#include <kis_display_color_converter.h>
#include <kis_canvas2.h>
#include "palettemodel.h"
#include "colorsetchooser.h"
#include "ui_wdgpalettedock.h"
/// The resource item delegate for rendering the resource preview
class PaletteDelegate : public QAbstractItemDelegate
{
public:
PaletteDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent), m_showText(false) {}
virtual ~PaletteDelegate() {}
/// reimplemented
virtual void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const;
/// reimplemented
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const {
return option.decorationSize;
}
void setShowText(bool showText) {
m_showText = showText;
}
private:
bool m_showText;
};
void PaletteDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
painter->save();
if (! index.isValid())
return;
if (option.state & QStyle::State_Selected) {
painter->setPen(QPen(option.palette.highlightedText(), 2.0));
painter->fillRect(option.rect, option.palette.highlight());
} else {
painter->setPen(QPen(option.palette.text(), 2.0));
}
QRect paintRect = option.rect.adjusted(1, 1, -1, -1);
QBrush brush = qVariantValue<QBrush>(index.data(Qt::BackgroundRole));
painter->fillRect(paintRect, brush);
painter->restore();
}
bool PaletteDockerDock::eventFilter(QObject* object, QEvent* event)
{
if (object == m_wdgPaletteDock->paletteView->viewport() && event->type() == QEvent::Wheel) {
QWheelEvent* qwheel = dynamic_cast<QWheelEvent* >(event);
if (qwheel->modifiers() & Qt::ControlModifier) {
int numDegrees = qwheel->delta() / 8;
int numSteps = numDegrees / 7;
int curSize = m_wdgPaletteDock->paletteView->horizontalHeader()->sectionSize(0);
int setSize = numSteps + curSize;
if ( setSize >= 12 ) {
m_wdgPaletteDock->paletteView->horizontalHeader()->setDefaultSectionSize(setSize);
m_wdgPaletteDock->paletteView->verticalHeader()->setDefaultSectionSize(setSize);
KisConfig cfg;
cfg.setPaletteDockerPaletteViewSectionSize(setSize);
}
return true;
} else {
return false;
}
} else {
return QWidget::eventFilter(object, event);
}
}
PaletteDockerDock::PaletteDockerDock( )
: QDockWidget(i18n("Palette"))
, m_wdgPaletteDock(new Ui_WdgPaletteDock())
, m_currentColorSet(0)
, m_resourceProvider(0)
+ , m_canvas(0)
{
QWidget* mainWidget = new QWidget(this);
setWidget(mainWidget);
m_wdgPaletteDock->setupUi(mainWidget);
m_wdgPaletteDock->bnAdd->setIcon(themedIcon("list-add"));
m_wdgPaletteDock->bnAdd->setIconSize(QSize(16, 16));
m_wdgPaletteDock->bnAddDialog->setIcon(themedIcon("document-new"));
m_wdgPaletteDock->bnAddDialog->setIconSize(QSize(16, 16));
m_wdgPaletteDock->bnRemove->setIcon(themedIcon("edit-delete"));
m_wdgPaletteDock->bnRemove->setIconSize(QSize(16, 16));
m_wdgPaletteDock->bnAdd->setEnabled(false);
m_wdgPaletteDock->bnRemove->setEnabled(false);
connect(m_wdgPaletteDock->bnAdd, SIGNAL(clicked(bool)), this, SLOT(addColorForeground()));
connect(m_wdgPaletteDock->bnAddDialog, SIGNAL(clicked(bool)), this, SLOT(addColor()));
connect(m_wdgPaletteDock->bnRemove, SIGNAL(clicked(bool)), this, SLOT(removeColor()));
m_model = new PaletteModel(this);
m_wdgPaletteDock->paletteView->setModel(m_model);
m_wdgPaletteDock->paletteView->setShowGrid(false);
m_wdgPaletteDock->paletteView->horizontalHeader()->setVisible(false);
m_wdgPaletteDock->paletteView->verticalHeader()->setVisible(false);
m_wdgPaletteDock->paletteView->setItemDelegate(new PaletteDelegate());
KisConfig cfg;
QPalette pal(palette());
pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor());
m_wdgPaletteDock->paletteView->setAutoFillBackground(true);
m_wdgPaletteDock->paletteView->setPalette(pal);
connect(m_wdgPaletteDock->paletteView, SIGNAL(clicked(QModelIndex)), this, SLOT(entrySelected(QModelIndex)));
m_wdgPaletteDock->paletteView->viewport()->installEventFilter(this);
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer(false);
m_serverAdapter = QSharedPointer<KoAbstractResourceServerAdapter>(new KoResourceServerAdapter<KoColorSet>(rServer));
m_serverAdapter->connectToResourceServer();
rServer->addObserver(this);
m_colorSetChooser = new ColorSetChooser(this);
connect(m_colorSetChooser, SIGNAL(paletteSelected(KoColorSet*)), this, SLOT(setColorSet(KoColorSet*)));
m_wdgPaletteDock->bnColorSets->setIcon(koIcon("hi16-palette_library"));
m_wdgPaletteDock->bnColorSets->setToolTip(i18n("Choose palette"));
m_wdgPaletteDock->bnColorSets->setPopupWidget(m_colorSetChooser);
int defaultSectionSize = cfg.paletteDockerPaletteViewSectionSize();
m_wdgPaletteDock->paletteView->horizontalHeader()->setDefaultSectionSize(defaultSectionSize);
m_wdgPaletteDock->paletteView->verticalHeader()->setDefaultSectionSize(defaultSectionSize);
QString defaultPalette = cfg.defaultPalette();
KoColorSet* defaultColorSet = rServer->resourceByName(defaultPalette);
if (defaultColorSet) {
setColorSet(defaultColorSet);
}
}
PaletteDockerDock::~PaletteDockerDock()
{
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
rServer->removeObserver(this);
if (m_currentColorSet) {
KisConfig cfg;
cfg.setDefaultPalette(m_currentColorSet->name());
}
}
void PaletteDockerDock::setMainWindow(KisViewManager* kisview)
{
m_resourceProvider = kisview->resourceProvider();
connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)), SLOT(saveToWorkspace(KisWorkspaceResource*)));
connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)), SLOT(loadFromWorkspace(KisWorkspaceResource*)));
kisview->nodeManager()->disconnect(m_model);
}
void PaletteDockerDock::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != 0);
if (canvas) {
KisCanvas2 *cv = dynamic_cast<KisCanvas2*>(canvas);
m_model->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface());
}
+ m_canvas = static_cast<KisCanvas2*>(canvas);
}
void PaletteDockerDock::unsetCanvas()
{
setEnabled(false);
m_model->setDisplayRenderer(0);
+ m_canvas = 0;
}
void PaletteDockerDock::unsetResourceServer()
{
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
rServer->removeObserver(this);
}
void PaletteDockerDock::removingResource(KoColorSet *resource)
{
if (resource == m_currentColorSet) {
setColorSet(0);
}
}
void PaletteDockerDock::resourceChanged(KoColorSet *resource)
{
setColorSet(resource);
}
void PaletteDockerDock::setColorSet(KoColorSet* colorSet)
{
m_model->setColorSet(colorSet);
if (colorSet && colorSet->removable()) {
m_wdgPaletteDock->bnAdd->setEnabled(true);
m_wdgPaletteDock->bnRemove->setEnabled(false);
} else {
m_wdgPaletteDock->bnAdd->setEnabled(false);
m_wdgPaletteDock->bnRemove->setEnabled(false);
}
m_currentColorSet = colorSet;
}
void PaletteDockerDock::addColorForeground()
{
if (m_resourceProvider) {
KoColorSetEntry newEntry;
newEntry.color = m_resourceProvider->fgColor();
m_currentColorSet->add(newEntry);
m_currentColorSet->save();
setColorSet(m_currentColorSet); // update model
}
}
void PaletteDockerDock::addColor()
{
-// if (m_currentColorSet && m_resourceProvider) {
-// const KoColorDisplayRendererInterface *displayRenderer =
-// m_canvas->displayColorConverter()->displayRendererInterface();
-
-// KoColor currentFgColor = m_canvas->resourceManager()->foregroundColor();
-// QColor color;
-
-// int result = KColorDialog::getColor(color, displayRenderer->toQColor(currentFgColor));
-
-// if (result == KColorDialog::Accepted) {
-// KoColorSetEntry newEntry;
-// newEntry.color = displayRenderer->approximateFromRenderedQColor(color);
-// m_currentColorSet->add(newEntry);
-// m_currentColorSet->save();
-// setColorSet(m_currentColorSet); // update model
-// }
-// }
+ if (m_currentColorSet && m_resourceProvider) {
+
+ const KoColorDisplayRendererInterface *displayRenderer =
+ m_canvas->displayColorConverter()->displayRendererInterface();
+
+ KoColor currentFgColor = m_canvas->resourceManager()->foregroundColor();
+ QColor color;
+
+ int result = KColorDialog::getColor(color, displayRenderer->toQColor(currentFgColor));
+
+ if (result == KColorDialog::Accepted) {
+ KoColorSetEntry newEntry;
+ newEntry.color = displayRenderer->approximateFromRenderedQColor(color);
+ m_currentColorSet->add(newEntry);
+ m_currentColorSet->save();
+ setColorSet(m_currentColorSet); // update model
+ }
+ }
}
void PaletteDockerDock::removeColor()
{
QModelIndex index = m_wdgPaletteDock->paletteView->currentIndex();
if (!index.isValid()) {
return;
}
int i = index.row()*m_model->columnCount()+index.column();
m_currentColorSet->removeAt(i);
m_currentColorSet->save();
setColorSet(m_currentColorSet); // update model
}
void PaletteDockerDock::entrySelected(QModelIndex index)
{
if (!index.isValid()) {
return;
}
int i = index.row()*m_model->columnCount()+index.column();
if (i < m_currentColorSet->nColors()) {
KoColorSetEntry entry = m_currentColorSet->getColor(i);
if (m_resourceProvider) {
m_resourceProvider->setFGColor(entry.color);
}
if (m_currentColorSet->removable()) {
m_wdgPaletteDock->bnRemove->setEnabled(true);
}
}
}
void PaletteDockerDock::saveToWorkspace(KisWorkspaceResource* workspace)
{
if (m_currentColorSet) {
workspace->setProperty("palette", m_currentColorSet->name());
}
}
void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResource* workspace)
{
if (workspace->hasProperty("palette")) {
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette"));
if (colorSet) {
setColorSet(colorSet);
}
}
}
diff --git a/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.h b/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.h
index adb23527eb7..10a07bbf218 100644
--- a/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.h
+++ b/krita/plugins/extensions/dockers/palettedocker/palettedocker_dock.h
@@ -1,81 +1,84 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PALETTEDOCKER_DOCK_H
#define PALETTEDOCKER_DOCK_H
#include <QDockWidget>
#include <QModelIndex>
#include <KoCanvasObserverBase.h>
#include <KoResourceServerAdapter.h>
#include <KoResourceServerObserver.h>
#include <KoColorSet.h>
#include <kis_mainwindow_observer.h>
class KisViewManager;
class KisCanvasResourceProvider;
+class KisCanvas2;
class KisWorkspaceResource;
class ColorSetChooser;
class PaletteModel;
class Ui_WdgPaletteDock;
+
class PaletteDockerDock : public QDockWidget, public KisMainwindowObserver, public KoResourceServerObserver<KoColorSet> {
Q_OBJECT
public:
PaletteDockerDock();
virtual ~PaletteDockerDock();
QString observerName() { return "PaletteDockerDock"; }
virtual void setMainWindow(KisViewManager* kisview);
virtual void setCanvas(KoCanvasBase *canvas);
virtual void unsetCanvas();
public: // KoResourceServerObserver
virtual void unsetResourceServer();
virtual void resourceAdded(KoColorSet *) {}
virtual void removingResource(KoColorSet *resource);
virtual void resourceChanged(KoColorSet *resource);
virtual void syncTaggedResourceView() {}
virtual void syncTagAddition(const QString&) {}
virtual void syncTagRemoval(const QString&) {}
private Q_SLOTS:
void addColorForeground();
void addColor();
void removeColor();
void entrySelected(QModelIndex index);
void setColorSet(KoColorSet* colorSet);
void saveToWorkspace(KisWorkspaceResource* workspace);
void loadFromWorkspace(KisWorkspaceResource* workspace);
virtual bool eventFilter(QObject*, QEvent*);
private:
Ui_WdgPaletteDock* m_wdgPaletteDock;
PaletteModel *m_model;
QSharedPointer<KoAbstractResourceServerAdapter> m_serverAdapter;
- KoColorSet* m_currentColorSet;
- ColorSetChooser* m_colorSetChooser;
- KisCanvasResourceProvider* m_resourceProvider;
+ KoColorSet *m_currentColorSet;
+ ColorSetChooser *m_colorSetChooser;
+ KisCanvasResourceProvider *m_resourceProvider;
+ KisCanvas2 *m_canvas;
};
#endif
diff --git a/krita/plugins/extensions/gmic/Command.cpp b/krita/plugins/extensions/gmic/Command.cpp
index 7b8c780d46f..12042de5f29 100644
--- a/krita/plugins/extensions/gmic/Command.cpp
+++ b/krita/plugins/extensions/gmic/Command.cpp
@@ -1,545 +1,545 @@
/*
* Copyright (c) 2013-2015 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <Command.h>
#include <QString>
#include <QStringList>
#include <QChar>
#include <QList>
#include <kis_debug.h>
#include <kis_gmic_parser.h>
#include <QWidget>
#include <QGridLayout>
#include <QLabel>
#include <qslider.h>
#include <QSpinBox>
#include <QComboBox>
#include <QStringListModel>
#include <Parameter.h>
Command::Command(Component* parent): m_parent(parent)
{
}
Command::~Command()
{
}
void Command::processCommandName(const QString& line)
{
QStringList splittedLine = line.split(":");
Q_ASSERT(splittedLine.size() == 2);
QString commandName = splittedLine.at(0);
setName(commandName.trimmed());
QStringList commands = splittedLine[1].split(",");
Q_ASSERT(commands.size() == 2);
m_command = commands.at(0).trimmed();
m_commandPreview = commands.at(1).trimmed();
QStringList splitted = m_commandPreview.split("(");
if (splitted.size() == 2)
{
m_commandPreview = splitted.at(0);
m_commandPreviewZoom = splitted.at(1);
m_commandPreviewZoom.chop(1);
}
}
// sep = separator(),
// Preview type = choice("Full","Forward horizontal","Forward vertical","Backward horizontal","Backward vertical","Duplicate horizontal","Duplicate vertical")
QStringList Command::breakIntoTokens(const QString &line, bool &lastTokenEnclosed)
{
QStringList result;
lastTokenEnclosed = false;
int index = 0;
int lastIndex = 0;
while (index < line.size())
{
// skip parameter name, find index of =
index = line.indexOf("=", index);
if (index == -1)
{
break;
}
// point to next character
index++;
// eat all spaces
index = skipWhitespace(line, index);
// skip type-definition name e.g. "int", "_note", etc.
const QChar underscore('_');
int helperIndex = index;
while (helperIndex < line.size())
{
const QChar c = line.at(helperIndex);
if (c.isLetter() || c == underscore)
{
helperIndex++;
}
else
{
break;
}
}
QString typeName = line.mid(index, helperIndex - index);
if (typeName.startsWith(underscore))
{
typeName.remove(0, 1);
}
if (!Parameter::isTypeDefined(typeName))
{
dbgPlugins << "Unknown type" << typeName << line;
}
index = helperIndex;
// skip all whitespaces, e.g. note = note ("Example")
index = skipWhitespace(line, index);
// determine separator
// Type separators '()' can be replaced by '[]' or '{}' if necessary ...
QChar delimiter = line.at(index);
QChar closingdelimiter;
switch (delimiter.toLatin1())
{
case '(':
{
closingdelimiter = ')';
break;
}
case '[':
{
closingdelimiter = ']';
break;
}
case '{':
{
closingdelimiter = '}';
break;
}
default:
{
dbgPlugins << "Delimiter: " << delimiter << line;
Q_ASSERT(false);
break;
}
}
// search for enclosing separator
while (index < line.size() - 1)
{
if (line.at(index) != closingdelimiter)
{
index++;
}
else
{
// we found enclosing separator
break;
}
}
if (line.at(index) != closingdelimiter)
{
lastTokenEnclosed = false;
//dbgPlugins << "Enclosing delimiter not found, trying again" << line.at(index);
break;
}
else
{
lastTokenEnclosed = true;
// "," removal only if there are n tokens in one line
if (lastIndex != 0 )
{
// if there are more tokens on one line, they are separated by ",", so remove it here
if (line.at(lastIndex) == ',')
{
lastIndex++;
}
}
QString token = line.mid(lastIndex, index + 1 - lastIndex).trimmed();
result.append(token);
lastIndex = index + 1;
}
} // while
return result;
}
bool Command::processParameter(const QStringList& block)
{
QString parameterLine = mergeBlockToLine(block);
// remove " :"
parameterLine = parameterLine.remove(0, 2).trimmed();
/* State: one parameter one line
* one parameter n lines
* n parameters one line
*/
// break into parameter tokens
bool lastTokenEnclosed = true;
QStringList tokens = breakIntoTokens(parameterLine, lastTokenEnclosed);
if (!lastTokenEnclosed)
{
// we need more lines of command parameters
//dbgPlugins << "ParameterLine not enclosed";
return false;
}
static int unhandledParameters = 0;
Parameter * parameter = 0;
foreach (QString token, tokens)
{
QString paramName;
QString typeDefinition;
// determine parameter name and parameter definition
processToken(token, paramName, typeDefinition);
bool showPreviewOnChange = true;
if (typeDefinition.startsWith("_"))
{
showPreviewOnChange = false;
typeDefinition.remove(0,1);
}
if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FLOAT_P]))
{
parameter = new FloatParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::INT_P]))
{
parameter = new IntParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::SEPARATOR_P]))
{
parameter = new SeparatorParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::CHOICE_P]))
{
parameter = new ChoiceParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::TEXT_P]))
{
parameter = new TextParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::NOTE_P]))
{
parameter = new NoteParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::LINK_P]))
{
parameter = new LinkParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::BOOL_P]))
{
parameter = new BoolParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::COLOR_P]))
{
parameter = new ColorParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FOLDER_P]))
{
parameter = new FolderParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::FILE_P]))
{
parameter = new FileParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::CONST_P]))
{
parameter = new ConstParameter(paramName, showPreviewOnChange);
}
else if (typeDefinition.startsWith(Parameter::PARAMETER_NAMES[Parameter::BUTTON_P]))
{
parameter = new ButtonParameter(paramName, showPreviewOnChange);
}
else
{
unhandledParameters++;
dbgPlugins << "Unhandled parameter " << unhandledParameters << parent()->name() << "\\" << name() << paramName << typeDefinition;
}
if (parameter)
{
parameter->parseValues(typeDefinition);
m_parameters.append(parameter);
parameter = 0;
}
}
// let's ignore tokens
return true;
}
void Command::add(Component* c)
{
Q_UNUSED(c);
}
void Command::print(int level)
{
for(int j=0; j < level; ++j)
{
dbgPlugins << "\t";
}
dbgPlugins << "Command : " << qPrintable(name());
foreach(Parameter * p, m_parameters)
{
for(int j=0; j < level+1; ++j)
{
dbgPlugins << "\t";
}
QString str = p->toString();
str.truncate(30);
dbgPlugins << str;
}
}
Component* Command::child(int index) const
{
Q_UNUSED(index);
return 0;
}
Component* Command::parent()
{
return m_parent;
}
int Command::row() const
{
if (m_parent)
{
m_parent->indexOf(const_cast<Command *>(this));
}
return 0;
}
QVariant Command::data(int column)
{
Q_UNUSED(column);
Q_ASSERT(column == 0);
return name();
}
int Command::childCount() const
{
return 0;
}
int Command::columnCount() const
{
return 1;
}
QString Command::buildCommand(const QString &baseCommand)
{
// build list of parameters
QString parameterList;
foreach(Parameter * p, m_parameters)
{
if (!p->value().isNull())
{
parameterList.append(p->value() +",");
}
else
{
if (!p->isPresentationalOnly())
{
// implement for given parameter value()!
dbgPlugins << "UNHANDLED command parameter: " << p->m_name << p->toString();
}
}
}
if (parameterList.endsWith(","))
{
parameterList.chop(1);
}
QString command = "-" + baseCommand;
// some commands might contain presentational parameters only
if (!parameterList.isEmpty())
{
command.append(" ");
command.append(parameterList);
}
return command;
}
void Command::writeConfiguration(KisGmicFilterSetting* setting)
{
QString command = buildCommand(m_command);
setting->setGmicCommand(command);
QString commandPreview = buildCommand(m_commandPreview);
setting->setPreviewGmicCommand(commandPreview);
}
QString Command::mergeBlockToLine(const QStringList& block)
{
Q_ASSERT(block.size() > 0);
if (block.size() == 1)
{
return block.at(0);
}
QString result = block.at(0);
for (int i = 1; i < block.size(); i++)
{
QString nextLine = block.at(i);
nextLine = nextLine.remove(0, 2).trimmed();
result = result + nextLine;
}
return result;
}
void Command::reset()
{
foreach(Parameter * p, m_parameters)
{
p->reset();
}
}
bool Command::processToken(const QString& token, QString& parameterName, QString& parameterDefinition)
{
// determine parameter name and parameter definition
// dbgPlugins << "Processing token " << token;
QString trimedToken = token.trimmed();
// '=' separates parameter name and parameter definition
// parameter definition can contain '=' so avoid split(") here
int firstSeparatorIndex = trimedToken.indexOf("=");
Q_ASSERT(firstSeparatorIndex != -1);
parameterName = trimedToken.left(firstSeparatorIndex).trimmed();
parameterDefinition = trimedToken.mid(firstSeparatorIndex + 1).trimmed();
//dbgPlugins << ppVar(parameterName);
//dbgPlugins << ppVar(parameterDefinition);
return true;
}
int Command::skipWhitespace(const QString& line, int index)
{
while (index < line.size())
{
if (line.at(index).isSpace())
{
index++;
}
else
{
break;
}
}
return index;
}
void Command::setParameter(const QString& name, const QString& value)
{
for (int i = 0; i < m_parameters.size(); i++)
{
if (m_parameters.at(i)->name() == name)
{
m_parameters[i]->setValue(value);
}
}
}
QString Command::parameter(const QString &name) const
{
for (int i = 0; i < m_parameters.size(); i++)
{
if (m_parameters.at(i)->name() == name)
{
return m_parameters.at(i)->value();
}
}
return QString();
}
bool Command::hasParameterName(const QString& paramName, const QString& paramType)
{
Parameter::ParameterType type = Parameter::INVALID_P;
if (!paramType.isEmpty())
{
type = Parameter::nameToType(paramType);
}
for (int i = 0; i < m_parameters.size(); i++)
{
Parameter * currentParameter = m_parameters.at(i);
if (currentParameter->name() == paramName)
{
// if not empty, we check type also
if (!paramType.isEmpty())
{
if (currentParameter->m_type == type)
{
return true;
}
else
{
- qDebug() << "Ignoring type " << currentParameter->m_type;
+ dbgKrita << "Ignoring type " << currentParameter->m_type;
}
}
else
{
return true;
}
}
}
return false;
}
diff --git a/krita/plugins/extensions/gmic/gmicparser.cpp b/krita/plugins/extensions/gmic/gmicparser.cpp
index 6cdad238a50..1fb3b2818e9 100644
--- a/krita/plugins/extensions/gmic/gmicparser.cpp
+++ b/krita/plugins/extensions/gmic/gmicparser.cpp
@@ -1,38 +1,38 @@
#include <QApplication>
#include <kis_gmic_parser.h>
#include <Command.h>
#include <kis_gmic_filter_model.h>
-#include <QDebug>
+#include <kis_debug.h>
#include <kis_gmic_widget.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStringList definitions;
if (argc > 1)
{
for (int i = 1; i < argc; i++)
{
definitions << QString::fromUtf8(argv[i]);
}
}
else
{
definitions << "~/.kde/share/apps/krita/gmic/gmic_def.gmic";
}
- qDebug() << definitions;
+ dbgKrita << definitions;
KisGmicParser parser(definitions);
Component * root = parser.createFilterTree();
KisGmicFilterModel * model = new KisGmicFilterModel(root);
KisGmicWidget gmicWidget(model);
gmicWidget.show();
return app.exec();
}
diff --git a/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp b/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp
index 5521902c508..8ddbb211059 100644
--- a/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp
+++ b/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp
@@ -1,49 +1,49 @@
#include "kis_filter_preview_widget.h"
#include <kis_canvas_widget_base.h>
#include <kis_config.h>
#include <QPaintEvent>
#include <QPainter>
#include <QWheelEvent>
-#include <QDebug>
+#include <kis_debug.h>
KisFilterPreviewWidget::KisFilterPreviewWidget(QWidget* parent): QWidget(parent)
{
KisConfig cfg;
// for preview make it smaller than on canvas
qint32 checkSize = qMax(1,cfg.checkSize() / 2);
QImage checkImage = KisCanvasWidgetBase::createCheckersImage(checkSize);
m_checkBrush = QBrush(checkImage);
m_pixmap = QPixmap::fromImage(checkImage);
}
KisFilterPreviewWidget::~KisFilterPreviewWidget()
{
}
void KisFilterPreviewWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter gc(this);
gc.fillRect(m_pixmap.rect(), m_checkBrush);
gc.drawPixmap(0,0,m_pixmap);
QPen pen(Qt::black, 1, Qt::SolidLine);
gc.setPen(pen);
gc.drawRect(m_pixmap.rect().adjusted(0,0,-1,-1));
}
QSize KisFilterPreviewWidget::sizeHint() const
{
return QSize(1, 1);
}
void KisFilterPreviewWidget::setImage(const QImage& img)
{
m_pixmap = QPixmap::fromImage(img);
update();
}
diff --git a/krita/plugins/extensions/gmic/kis_gmic_blacklister.cpp b/krita/plugins/extensions/gmic/kis_gmic_blacklister.cpp
index d24746f1cc4..40f4e345569 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_blacklister.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_blacklister.cpp
@@ -1,350 +1,350 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_gmic_blacklister.h"
#include "kis_debug.h"
#include <QFile>
#include <QQueue>
#include <QStack>
#include <QDir>
#include <QDomDocument>
#include <qdom.h>
#include <QTextDocument>
#include <Component.h>
#include "Command.h"
#include "Category.h"
KisGmicBlacklister::KisGmicBlacklister(const QString& filePath):m_fileName(filePath)
{
parseBlacklist();
}
KisGmicBlacklister::~KisGmicBlacklister()
{
}
bool KisGmicBlacklister::parseBlacklist()
{
QDomDocument doc("mydocument");
QFile file(m_fileName);
if (!file.open(QIODevice::ReadOnly))
{
return false;
}
if (!doc.setContent(&file))
{
file.close();
warnPlugins << m_fileName << " has wrong format? Correct XML expected!";
return false;
}
QDomElement docElem = doc.documentElement();
if (docElem.tagName() != "blacklist")
{
// weird xml
return false;
}
// iterate categories
QDomNodeList nodeList = docElem.elementsByTagName("category");
for(int i = 0;i < nodeList.count(); i++)
{
QDomElement categoryElement = nodeList.at(i).toElement();
QString categoryName = categoryElement.attribute("name");
// iterate filters
QDomNodeList filterNodeList = categoryElement.elementsByTagName("filter");
for (int j = 0; j < filterNodeList.count(); j++)
{
QDomElement filterElement = filterNodeList.at(j).toElement();
QString filterName = filterElement.attribute("name");
m_categoryNameBlacklist[ categoryName ].insert(filterName);
}
}
return true;
}
void KisGmicBlacklister::dump()
{
QList<QString> keysList = m_categoryNameBlacklist.keys();
foreach (const QString& item, keysList)
{
QSet<QString> filters = m_categoryNameBlacklist[item];
dbgPlugins << item;
foreach (const QString filterItem, filters)
{
dbgPlugins << "\t" << filterItem;
}
}
}
bool KisGmicBlacklister::isBlacklisted(const QString& filterName, const QString& filterCategory)
{
// transform input
QString filterNamePlain = toPlainText(filterName);
QString filterCategoryPlain = toPlainText(filterCategory);
if (!m_categoryNameBlacklist.contains(filterCategoryPlain))
{
return false;
}
QSet<QString> filters = m_categoryNameBlacklist[ filterCategoryPlain ];
return filters.contains(filterNamePlain);
}
QString KisGmicBlacklister::toPlainText(const QString& htmlText)
{
QTextDocument doc;
doc.setHtml(htmlText);
return doc.toPlainText();
}
Command* KisGmicBlacklister::findFilter(const Component* rootNode, const QString& filterCategory, const QString& filterName)
{
Command * result = 0;
QQueue<const Component *> q;
q.enqueue(rootNode);
while (!q.isEmpty())
{
Component * c = const_cast<Component *>( q.dequeue() );
if (c->childCount() == 0)
{
// check filtername
if (toPlainText(c->name()) == filterName)
{
// check category
if (toPlainText(c->parent()->name()) == filterCategory)
{
result = static_cast<Command *>(c);
break;
}
}
else
{
- //qDebug() << c->name() << "is different from " << filterName;
+ //dbgKrita << c->name() << "is different from " << filterName;
}
}
else
{
for (int i=0; i < c->childCount(); i++)
{
q.enqueue(c->child(i));
}
}
}
return result;
}
Component* KisGmicBlacklister::findFilterByPath(const Component* rootNode, const Component * path)
{
const Component * pathIter = path;
const Component * rootIter = rootNode;
while ((pathIter->childCount() > 0) && (rootIter->childCount() > 0))
{
const Category * rootCategory = static_cast<const Category *>(rootIter);
int indexOfItem = rootCategory->indexOf<Category>(pathIter->name());
if ((indexOfItem > -1) && (indexOfItem < rootIter->childCount()))
{
rootIter = rootIter->child(indexOfItem);
Q_ASSERT(pathIter->childCount() == 1);
pathIter = pathIter->child(0);
if (pathIter->childCount() == 0 && rootIter->childCount() > 0)
{
int index = static_cast<const Category *>(rootIter)->indexOf<Command>(pathIter->name());
if (index != -1)
{
return rootIter->child(index);
}
}
}
else
{
break;
}
}
return 0;
}
QList<Command*> KisGmicBlacklister::findFilterByParamName(const Component* rootNode, const QString& paramName, const QString& paramType)
{
QList<Command*> commands;
ComponentIterator it(rootNode);
while (it.hasNext())
{
Component * component = const_cast<Component *>( it.next() );
if (component->childCount() == 0)
{
Command * cmd = static_cast<Command *>(component);
if (cmd->hasParameterName(paramName, paramType))
{
commands.append(cmd);
}
}
}
return commands;
}
QVector<Command*> KisGmicBlacklister::filtersByName(const Component* rootNode, const QString& filterName)
{
QVector<Command *> commands;
ComponentIterator it(rootNode);
while (it.hasNext())
{
Component * component = const_cast<Component *>( it.next() );
if (component->childCount() == 0)
{
Command * cmd = static_cast<Command *>(component);
if (toPlainText(cmd->name()) == filterName)
{
commands.append(cmd);
}
}
}
return commands;
}
QDomDocument KisGmicBlacklister::dumpFiltersToXML(const Component* rootNode)
{
ComponentIterator it(rootNode);
QDomDocument doc;
doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""));
QDomElement root = doc.createElement("filters");
doc.appendChild(root);
while (it.hasNext())
{
Component * component = const_cast<Component *>( it.next() );
if (component->childCount() == 0)
{
QStack<QString> pathStack;
Component * parent = component->parent();
while (parent != 0)
{
pathStack.push( /*toPlainText*/(parent->name()) );
parent = parent->parent();
}
QStringList categoryPath;
while (!pathStack.isEmpty())
{
categoryPath.push_back(pathStack.pop());
}
QDomElement parentElem = root;
for (int i = 0; i < categoryPath.size(); i++)
{
// add categories if needed
bool alreadyExists = false;
QString categoryName = /*toPlainText*/(categoryPath.at(i));
QDomNodeList elems = parentElem.elementsByTagName("category");
for (int i = 0; i < elems.size(); i++)
{
QDomElement categoryElem = elems.at(i).toElement();
QDomAttr attr = categoryElem.attributeNode("name");
if (attr.value() == categoryName)
{
parentElem = categoryElem;
alreadyExists = true;
break;
}
}
if (!alreadyExists)
{
QDomElement newCategory = doc.createElement("category");
newCategory.setAttribute("name", categoryName);
parentElem.appendChild(newCategory);
parentElem = newCategory;
}
}
KisGmicFilterSetting settings;
Command * cmd = static_cast<Command *>(component);
cmd->writeConfiguration(&settings);
QString filterCommand = settings.gmicCommand();
filterCommand = filterCommand.replace(QDir::homePath(), QLatin1String("[[:home:]]"));
QString filterName = /*toPlainText*/(component->name());
QDomElement filterElem = doc.createElement("filter");
filterElem.setAttribute("name", filterName);
QDomCDATASection cdata = doc.createCDATASection(filterCommand);
QDomElement cmdElem = doc.createElement("gmicCommand");
cmdElem.appendChild(cdata);
filterElem.appendChild(cmdElem);
parentElem.appendChild(filterElem);
}
}
return doc;
}
// *** ComponentIterator *** TODO: move to own file or to Component
ComponentIterator::ComponentIterator(const Component* c)
{
if (c)
{
m_queue.enqueue(c);
}
}
bool ComponentIterator::hasNext() const
{
return !m_queue.isEmpty();
}
const Component* ComponentIterator::next()
{
if (hasNext())
{
const Component* c = m_queue.dequeue();
for (int i=0; i < c->childCount(); i++)
{
m_queue.enqueue(c->child(i));
}
return c;
}
return 0;
}
ComponentIterator::~ComponentIterator()
{
m_queue.clear();
}
diff --git a/krita/plugins/extensions/gmic/kis_gmic_filter_proxy_model.cpp b/krita/plugins/extensions/gmic/kis_gmic_filter_proxy_model.cpp
index e9d39fa4dd1..62d08916474 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_filter_proxy_model.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_filter_proxy_model.cpp
@@ -1,55 +1,55 @@
/*
* Copyright (c) 2014 Lukáš Tvrdý <lukast.dev@gmail.com
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kis_gmic_filter_proxy_model.h>
#include "kis_gmic_filter_model.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QQueue>
KisGmicFilterProxyModel::KisGmicFilterProxyModel(QObject* parent): QSortFilterProxyModel(parent)
{
}
bool KisGmicFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QQueue<QModelIndex> q;
q.enqueue(index);
while (!q.isEmpty())
{
QModelIndex item = q.dequeue();
if ( item.data(Qt::DisplayRole).toString().contains( filterRegExp() ) )
{
return true;
}
else
{
int rowCount = sourceModel()->rowCount(item);
for (int row = 0; row < rowCount; row++)
{
q.enqueue( item.child(row, 0) );
}
}
}
return false;
}
diff --git a/krita/plugins/extensions/gmic/kis_gmic_simple_convertor.cpp b/krita/plugins/extensions/gmic/kis_gmic_simple_convertor.cpp
index d865e1c20cf..0f6461231d8 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_simple_convertor.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_simple_convertor.cpp
@@ -1,872 +1,872 @@
/*
* Copyright (c) 2013 Lukáš Tvrdý <lukast.dev@gmail.com
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kis_gmic_simple_convertor.h>
#include <kis_debug.h>
#include <kis_random_accessor_ng.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpace.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpaceTraits.h>
using namespace cimg_library;
#define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v )
#define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v )
template<typename _channel_type_, typename traits>
class KisColorToFloatConvertor : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisColorToFloatConvertor(){}
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
{
const RGBPixel* srcPixel = reinterpret_cast<const RGBPixel*>(src);
KoRgbF32Traits::Pixel* dstPixel = reinterpret_cast<KoRgbF32Traits::Pixel*>(dst);
while(nPixels > 0)
{
dstPixel->red = SCALE_TO_FLOAT(srcPixel->red);
dstPixel->green = SCALE_TO_FLOAT(srcPixel->green);
dstPixel->blue = SCALE_TO_FLOAT(srcPixel->blue);
dstPixel->alpha = SCALE_TO_FLOAT(srcPixel->alpha);
--nPixels;
++srcPixel;
++dstPixel;
}
}
};
template<typename _channel_type_, typename traits>
class KisColorFromFloat : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisColorFromFloat(float gmicUnitValue = 255.0f):m_gmicUnitValue(gmicUnitValue){}
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
{
const KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<const KoRgbF32Traits::Pixel*>(src);
RGBPixel* dstPixel = reinterpret_cast<RGBPixel*>(dst);
float gmicUnitValue2KritaUnitValue = KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
while(nPixels > 0)
{
dstPixel->red = SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
dstPixel->green = SCALE_FROM_FLOAT(srcPixel->green * gmicUnitValue2KritaUnitValue);
dstPixel->blue = SCALE_FROM_FLOAT(srcPixel->blue * gmicUnitValue2KritaUnitValue);
dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha * gmicUnitValue2KritaUnitValue);
--nPixels;
++srcPixel;
++dstPixel;
}
}
private:
float m_gmicUnitValue;
};
template<typename _channel_type_, typename traits>
class KisColorFromGrayScaleFloat : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisColorFromGrayScaleFloat(float gmicUnitValue = 255.0f):m_gmicUnitValue(gmicUnitValue){}
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
{
const KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<const KoRgbF32Traits::Pixel*>(src);
RGBPixel* dstPixel = reinterpret_cast<RGBPixel*>(dst);
float gmicUnitValue2KritaUnitValue = KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
// warning: green and blue channels on input contain random data!!! see that we copy only one channel
// when gmic image has grayscale colorspace
while(nPixels > 0)
{
dstPixel->red = dstPixel->green = dstPixel->blue = SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->alpha * gmicUnitValue2KritaUnitValue);
--nPixels;
++srcPixel;
++dstPixel;
}
}
private:
float m_gmicUnitValue;
};
template<typename _channel_type_, typename traits>
class KisColorFromGrayScaleAlphaFloat : public KoColorTransformation
{
typedef traits RGBTrait;
typedef typename RGBTrait::Pixel RGBPixel;
public:
KisColorFromGrayScaleAlphaFloat(float gmicUnitValue = 255.0f):m_gmicUnitValue(gmicUnitValue){}
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const
{
const KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<const KoRgbF32Traits::Pixel*>(src);
RGBPixel* dstPixel = reinterpret_cast<RGBPixel*>(dst);
float gmicUnitValue2KritaUnitValue = KoColorSpaceMathsTraits<float>::unitValue / m_gmicUnitValue;
// warning: green and blue channels on input contain random data!!! see that we copy only one channel
// when gmic image has grayscale colorspace
while(nPixels > 0)
{
dstPixel->red = dstPixel->green = dstPixel->blue = SCALE_FROM_FLOAT(srcPixel->red * gmicUnitValue2KritaUnitValue);
dstPixel->alpha = SCALE_FROM_FLOAT(srcPixel->green * gmicUnitValue2KritaUnitValue);
--nPixels;
++srcPixel;
++dstPixel;
}
}
private:
float m_gmicUnitValue;
};
static KoColorTransformation* createTransformationFromGmic(const KoColorSpace* colorSpace, quint32 gmicSpectrum,float gmicUnitValue)
{
KoColorTransformation * colorTransformation = 0;
if (colorSpace->colorModelId() != RGBAColorModelID)
{
- kWarning() << "Unsupported color space for fast pixel tranformation to gmic pixel format" << colorSpace->id();
+ dbgKrita << "Unsupported color space for fast pixel tranformation to gmic pixel format" << colorSpace->id();
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
if (gmicSpectrum == 3 || gmicSpectrum == 4)
{
colorTransformation = new KisColorFromFloat< float, KoRgbTraits < float > >(gmicUnitValue);
}else if (gmicSpectrum == 1)
{
colorTransformation = new KisColorFromGrayScaleFloat<float, KoRgbTraits < float > >(gmicUnitValue);
}else if (gmicSpectrum == 2)
{
colorTransformation = new KisColorFromGrayScaleAlphaFloat<float, KoRgbTraits < float > >(gmicUnitValue);
}
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
if (gmicSpectrum == 3 || gmicSpectrum == 4)
{
colorTransformation = new KisColorFromFloat< half, KoRgbTraits < half > >(gmicUnitValue);
}else if (gmicSpectrum == 1)
{
colorTransformation = new KisColorFromGrayScaleFloat< half, KoRgbTraits < half > >(gmicUnitValue);
}else if (gmicSpectrum == 2)
{
colorTransformation = new KisColorFromGrayScaleAlphaFloat< half, KoRgbTraits < half > >(gmicUnitValue);
}
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
if (gmicSpectrum == 3 || gmicSpectrum == 4)
{
colorTransformation = new KisColorFromFloat< quint16, KoBgrTraits < quint16 > >(gmicUnitValue);
}else if (gmicSpectrum == 1)
{
colorTransformation = new KisColorFromGrayScaleFloat< quint16, KoBgrTraits < quint16 > >(gmicUnitValue);
}else if (gmicSpectrum == 2)
{
colorTransformation = new KisColorFromGrayScaleAlphaFloat< quint16, KoBgrTraits < quint16 > >(gmicUnitValue);
}
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
if (gmicSpectrum == 3 || gmicSpectrum == 4)
{
colorTransformation = new KisColorFromFloat< quint8, KoBgrTraits < quint8 > >(gmicUnitValue);
}else if (gmicSpectrum == 1)
{
colorTransformation = new KisColorFromGrayScaleFloat< quint8, KoBgrTraits < quint8 > >(gmicUnitValue);
}else if (gmicSpectrum == 2)
{
colorTransformation = new KisColorFromGrayScaleAlphaFloat< quint8, KoBgrTraits < quint8 > >(gmicUnitValue);
}
} else
{
- kWarning() << "Unsupported color space " << colorSpace->id() << " for fast pixel tranformation to gmic pixel format";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " for fast pixel tranformation to gmic pixel format";
return 0;
}
return colorTransformation;
}
static KoColorTransformation* createTransformation(const KoColorSpace* colorSpace)
{
KoColorTransformation * colorTransformation = 0;
if (colorSpace->colorModelId() != RGBAColorModelID)
{
- kWarning() << "Unsupported color space for fast pixel tranformation to gmic pixel format" << colorSpace->id();
+ dbgKrita << "Unsupported color space for fast pixel tranformation to gmic pixel format" << colorSpace->id();
return 0;
}
if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
colorTransformation = new KisColorToFloatConvertor< float, KoRgbTraits < float > >();
}
#ifdef HAVE_OPENEXR
else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
colorTransformation = new KisColorToFloatConvertor< half, KoRgbTraits < half > >();
}
#endif
else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
colorTransformation = new KisColorToFloatConvertor< quint16, KoBgrTraits < quint16 > >();
} else if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
colorTransformation = new KisColorToFloatConvertor< quint8, KoBgrTraits < quint8 > >();
} else {
- kWarning() << "Unsupported color space " << colorSpace->id() << " for fast pixel tranformation to gmic pixel format";
+ dbgKrita << "Unsupported color space " << colorSpace->id() << " for fast pixel tranformation to gmic pixel format";
return 0;
}
return colorTransformation;
}
void KisGmicSimpleConvertor::convertFromGmicFast(gmic_image<float>& gmicImage, KisPaintDeviceSP dst, float gmicUnitValue)
{
const KoColorSpace * dstColorSpace = dst->colorSpace();
KoColorTransformation * gmicToDstPixelFormat = createTransformationFromGmic(dstColorSpace,gmicImage._spectrum,gmicUnitValue);
if (gmicToDstPixelFormat == 0)
{
dbgPlugins << "Fall-back to slow color conversion";
convertFromGmicImage(gmicImage, dst, gmicUnitValue);
return;
}
qint32 x = 0;
qint32 y = 0;
qint32 width = gmicImage._width;
qint32 height = gmicImage._height;
width = width < 0 ? 0 : width;
height = height < 0 ? 0 : height;
const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
KoColorSpaceRegistry::instance()->rgb8()->profile());
// this function always convert to rgba or rgb with various color depth
quint32 dstNumChannels = rgbaFloat32bitcolorSpace->channelCount();
// number of channels that we will copy
quint32 numChannels = gmicImage._spectrum;
// gmic image has 4, 3, 2, 1 channel
QVector<float *> planes(dstNumChannels);
int channelOffset = gmicImage._width * gmicImage._height;
for (unsigned int channelIndex = 0; channelIndex < gmicImage._spectrum; channelIndex++)
{
planes[channelIndex] = gmicImage._data + channelOffset * channelIndex;
}
for (unsigned int channelIndex = gmicImage._spectrum; channelIndex < dstNumChannels; channelIndex++)
{
planes[channelIndex] = 0; //turn off
}
qint32 dataY = 0;
qint32 imageY = y;
qint32 rowsRemaining = height;
const qint32 floatPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
KisRandomAccessorSP it = dst->createRandomAccessorNG(dst->x(), dst->y()); // 0,0
int tileWidth = it->numContiguousColumns(dst->x());
int tileHeight = it->numContiguousRows(dst->y());
Q_ASSERT(tileWidth == 64);
Q_ASSERT(tileHeight == 64);
quint8 * convertedTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight];
// grayscale and rgb case does not have alpha, so let's fill 4th channel of rgba tile with opacity opaque
if (gmicImage._spectrum == 1 || gmicImage._spectrum == 3)
{
quint32 nPixels = tileWidth * tileHeight;
quint32 pixelIndex = 0;
KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<KoRgbF32Traits::Pixel*>(convertedTile);
while (pixelIndex < nPixels)
{
srcPixel->alpha = gmicUnitValue;
++srcPixel;
++pixelIndex;
}
}
while (rowsRemaining > 0) {
qint32 dataX = 0;
qint32 imageX = x;
qint32 columnsRemaining = width;
qint32 numContiguousImageRows = it->numContiguousRows(imageY);
qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousImageColumns = it->numContiguousColumns(imageX);
qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining);
const qint32 dataIdx = dataX + dataY * width;
const qint32 tileRowStride = (tileWidth - columnsToWork) * floatPixelSize;
quint8 *tileItStart = convertedTile;
// copy gmic channels to float tile
qint32 channelSize = sizeof(float);
for(quint32 i=0; i<numChannels; i++)
{
float * planeIt = planes[i] + dataIdx;
qint32 dataStride = (width - columnsToWork);
quint8* tileIt = tileItStart;
for (qint32 row = 0; row < rowsToWork; row++) {
for (int col = 0; col < columnsToWork; col++) {
memcpy(tileIt, planeIt, channelSize);
tileIt += floatPixelSize;
planeIt += 1;
}
tileIt += tileRowStride;
planeIt += dataStride;
}
tileItStart += channelSize;
}
it->moveTo(imageX, imageY);
quint8 *dstTileItStart = it->rawData();
tileItStart = convertedTile; // back to the start of the converted tile
// copy float tile to dst colorspace based on input colorspace (rgb or grayscale)
for (qint32 row = 0; row < rowsToWork; row++)
{
gmicToDstPixelFormat->transform(tileItStart, dstTileItStart, columnsToWork);
dstTileItStart += dstColorSpace->pixelSize() * tileWidth;
tileItStart += floatPixelSize * tileWidth;
}
imageX += columnsToWork;
dataX += columnsToWork;
columnsRemaining -= columnsToWork;
}
imageY += rowsToWork;
dataY += rowsToWork;
rowsRemaining -= rowsToWork;
}
delete [] convertedTile;
delete gmicToDstPixelFormat;
}
void KisGmicSimpleConvertor::convertToGmicImageFast(KisPaintDeviceSP dev, CImg< float >& gmicImage, QRect rc)
{
KoColorTransformation * pixelToGmicPixelFormat = createTransformation(dev->colorSpace());
if (pixelToGmicPixelFormat == 0)
{
dbgPlugins << "Fall-back to slow color conversion method";
convertToGmicImage(dev, gmicImage, rc);
return;
}
if (rc.isEmpty())
{
dbgPlugins << "Image rectangle is empty! Using supplied gmic layer dimension";
rc = QRect(0,0,gmicImage._width, gmicImage._height);
}
qint32 x = rc.x();
qint32 y = rc.y();
qint32 width = rc.width();
qint32 height = rc.height();
width = width < 0 ? 0 : width;
height = height < 0 ? 0 : height;
const qint32 numChannels = 4;
int greenOffset = gmicImage._width * gmicImage._height;
int blueOffset = greenOffset * 2;
int alphaOffset = greenOffset * 3;
QVector<float *> planes;
planes.append(gmicImage._data);
planes.append(gmicImage._data + greenOffset);
planes.append(gmicImage._data + blueOffset);
planes.append(gmicImage._data + alphaOffset);
KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(dev->x(), dev->y());
int tileWidth = it->numContiguousColumns(dev->x());
int tileHeight = it->numContiguousRows(dev->y());
Q_ASSERT(tileWidth == 64);
Q_ASSERT(tileHeight == 64);
const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
KoColorSpaceRegistry::instance()->rgb8()->profile());
Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
const qint32 dstPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
const qint32 srcPixelSize = dev->pixelSize();
quint8 * dstTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight];
qint32 dataY = 0;
qint32 imageX = x;
qint32 imageY = y;
it->moveTo(imageX, imageY);
qint32 rowsRemaining = height;
while (rowsRemaining > 0) {
qint32 dataX = 0;
imageX = x;
qint32 columnsRemaining = width;
qint32 numContiguousImageRows = it->numContiguousRows(imageY);
qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining);
qint32 convertedTileY = tileHeight - rowsToWork;
Q_ASSERT(convertedTileY >= 0);
while (columnsRemaining > 0) {
qint32 numContiguousImageColumns = it->numContiguousColumns(imageX);
qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining);
qint32 convertedTileX = tileWidth - columnsToWork;
Q_ASSERT(convertedTileX >= 0);
const qint32 dataIdx = dataX + dataY * width;
const qint32 dstTileIndex = convertedTileX + convertedTileY * tileWidth;
const qint32 tileRowStride = (tileWidth - columnsToWork) * dstPixelSize;
const qint32 srcTileRowStride = (tileWidth - columnsToWork) * srcPixelSize;
it->moveTo(imageX, imageY);
quint8 *tileItStart = dstTile + dstTileIndex * dstPixelSize;
// transform tile row by row
quint8 *dstTileIt = tileItStart;
quint8 *srcTileIt = const_cast<quint8*>(it->rawDataConst());
qint32 row = rowsToWork;
while (row > 0)
{
pixelToGmicPixelFormat->transform(srcTileIt, dstTileIt , columnsToWork);
srcTileIt += columnsToWork * srcPixelSize;
srcTileIt += srcTileRowStride;
dstTileIt += columnsToWork * dstPixelSize;
dstTileIt += tileRowStride;
row--;
}
// here we want to copy floats to dstTile, so tileItStart has to point to float buffer
qint32 channelSize = sizeof(float);
for(qint32 i=0; i<numChannels;i++)
{
float * planeIt = planes[i] + dataIdx;
qint32 dataStride = (width - columnsToWork);
quint8* tileIt = tileItStart;
for (qint32 row = 0; row < rowsToWork; row++) {
for (int col = 0; col < columnsToWork; col++) {
memcpy(planeIt, tileIt, channelSize);
tileIt += dstPixelSize;
planeIt += 1;
}
tileIt += tileRowStride;
planeIt += dataStride;
}
// skip channel in tile: red, green, blue, alpha
tileItStart += channelSize;
}
imageX += columnsToWork;
dataX += columnsToWork;
columnsRemaining -= columnsToWork;
}
imageY += rowsToWork;
dataY += rowsToWork;
rowsRemaining -= rowsToWork;
}
delete [] dstTile;
delete pixelToGmicPixelFormat;
}
// gmic assumes float rgba in 0.0 - 255.0, thus default value
void KisGmicSimpleConvertor::convertToGmicImage(KisPaintDeviceSP dev, gmic_image<float>& gmicImage, QRect rc)
{
Q_ASSERT(!dev.isNull());
Q_ASSERT(gmicImage._spectrum == 4); // rgba
if (rc.isEmpty())
{
rc = QRect(0,0,gmicImage._width, gmicImage._height);
}
const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
KoColorSpaceRegistry::instance()->rgb8()->profile());
Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
int greenOffset = gmicImage._width * gmicImage._height;
int blueOffset = greenOffset * 2;
int alphaOffset = greenOffset * 3;
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags;
const KoColorSpace * colorSpace = dev->colorSpace();
KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(0,0);
int optimalBufferSize = 64; // most common numContiguousColumns, tile size?
quint8 * floatRGBApixel = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize];
quint32 pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
int pos = 0;
for (int y = 0; y < rc.height(); y++)
{
int x = 0;
while (x < rc.width())
{
it->moveTo(x, y);
qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);
colorSpace->convertPixelsTo(it->rawDataConst(), floatRGBApixel, rgbaFloat32bitcolorSpace, numContiguousColumns, renderingIntent, conversionFlags);
pos = y * gmicImage._width + x;
for (qint32 bx = 0; bx < numContiguousColumns; bx++)
{
memcpy(gmicImage._data + pos ,floatRGBApixel + bx * pixelSize , 4);
memcpy(gmicImage._data + pos + greenOffset ,floatRGBApixel + bx * pixelSize + 4, 4);
memcpy(gmicImage._data + pos + blueOffset ,floatRGBApixel + bx * pixelSize + 8, 4);
memcpy(gmicImage._data + pos + alphaOffset ,floatRGBApixel + bx * pixelSize + 12, 4);
pos++;
}
x += numContiguousColumns;
}
}
delete [] floatRGBApixel;
}
void KisGmicSimpleConvertor::convertFromGmicImage(gmic_image<float>& gmicImage, KisPaintDeviceSP dst, float gmicMaxChannelValue)
{
Q_ASSERT(!dst.isNull());
const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
KoColorSpaceRegistry::instance()->rgb8()->profile());
const KoColorSpace *dstColorSpace = dst->colorSpace();
if (dstColorSpace == 0)
{
dstColorSpace = rgbaFloat32bitcolorSpace;
}
KisPaintDeviceSP dev = dst;
int greenOffset = gmicImage._width * gmicImage._height;
int blueOffset = greenOffset * 2;
int alphaOffset = greenOffset * 3;
QRect rc(0,0,gmicImage._width, gmicImage._height);
KisRandomAccessorSP it = dev->createRandomAccessorNG(0,0);
int pos;
float r,g,b,a;
int optimalBufferSize = 64; // most common numContiguousColumns, tile size?
quint8 * floatRGBApixel = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize];
quint32 pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags;
// Krita needs rgba in 0.0...1.0
float multiplied = KoColorSpaceMathsTraits<float>::unitValue / gmicMaxChannelValue;
switch (gmicImage._spectrum)
{
case 1:
{
// convert grayscale to rgba
for (int y = 0; y < rc.height(); y++)
{
int x = 0;
while (x < rc.width())
{
it->moveTo(x, y);
qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);
pos = y * gmicImage._width + x;
for (qint32 bx = 0; bx < numContiguousColumns; bx++)
{
r = g = b = gmicImage._data[pos] * multiplied;
a = KoColorSpaceMathsTraits<float>::unitValue;
memcpy(floatRGBApixel + bx * pixelSize, &r,4);
memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4);
memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4);
memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4);
pos++;
}
rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags);
x += numContiguousColumns;
}
}
break;
}
case 2:
{
// convert grayscale alpha to rgba
for (int y = 0; y < rc.height(); y++)
{
int x = 0;
while (x < rc.width())
{
it->moveTo(x, y);
qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);
pos = y * gmicImage._width + x;
for (qint32 bx = 0; bx < numContiguousColumns; bx++)
{
r = g = b = gmicImage._data[pos] * multiplied;
a = gmicImage._data[pos + greenOffset] * multiplied;
memcpy(floatRGBApixel + bx * pixelSize, &r,4);
memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4);
memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4);
memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4);
pos++;
}
rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags);
x += numContiguousColumns;
}
}
break;
}
case 3:
{
// convert rgb -> rgba
for (int y = 0; y < rc.height(); y++)
{
int x = 0;
while (x < rc.width())
{
it->moveTo(x, y);
qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);
pos = y * gmicImage._width + x;
for (qint32 bx = 0; bx < numContiguousColumns; bx++)
{
r = gmicImage._data[pos] * multiplied;
g = gmicImage._data[pos + greenOffset] * multiplied;
b = gmicImage._data[pos + blueOffset ] * multiplied;
a = gmicMaxChannelValue * multiplied;
memcpy(floatRGBApixel + bx * pixelSize, &r,4);
memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4);
memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4);
memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4);
pos++;
}
rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags);
x += numContiguousColumns;
}
}
break;
}
case 4:
{
for (int y = 0; y < rc.height(); y++)
{
int x = 0;
while (x < rc.width())
{
it->moveTo(x, y);
qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);
pos = y * gmicImage._width + x;
for (qint32 bx = 0; bx < numContiguousColumns; bx++)
{
r = gmicImage._data[pos] * multiplied;
g = gmicImage._data[pos + greenOffset] * multiplied;
b = gmicImage._data[pos + blueOffset ] * multiplied;
a = gmicImage._data[pos + alphaOffset] * multiplied;
memcpy(floatRGBApixel + bx * pixelSize, &r,4);
memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4);
memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4);
memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4);
pos++;
}
rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags);
x += numContiguousColumns;
}
}
break;
}
default:
{
dbgPlugins << "Unsupported gmic output format : " << gmicImage._width << gmicImage._height << gmicImage._depth << gmicImage._spectrum;
}
}
}
QImage KisGmicSimpleConvertor::convertToQImage(gmic_image<float>& gmicImage, float gmicActualMaxChannelValue)
{
QImage image = QImage(gmicImage._width, gmicImage._height, QImage::Format_ARGB32);
dbgPlugins << image.format() <<"first pixel:"<< gmicImage._data[0] << gmicImage._width << gmicImage._height << gmicImage._spectrum;
int greenOffset = gmicImage._width * gmicImage._height;
int blueOffset = greenOffset * 2;
int pos = 0;
// always put 255 to qimage
float multiplied = 255.0f / gmicActualMaxChannelValue;
for (unsigned int y = 0; y < gmicImage._height; y++)
{
QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
for (unsigned int x = 0; x < gmicImage._width; x++)
{
pos = y * gmicImage._width + x;
float r = gmicImage._data[pos] * multiplied;
float g = gmicImage._data[pos + greenOffset] * multiplied;
float b = gmicImage._data[pos + blueOffset] * multiplied;
pixel[x] = qRgb(int(r),int(g), int(b));
}
}
return image;
}
void KisGmicSimpleConvertor::convertFromQImage(const QImage& image, CImg< float >& gmicImage, float gmicUnitValue)
{
int greenOffset = gmicImage._width * gmicImage._height;
int blueOffset = greenOffset * 2;
int alphaOffset = greenOffset * 3;
int pos = 0;
// QImage has 0..255
float qimageUnitValue = 255.0f;
float multiplied = gmicUnitValue / qimageUnitValue;
Q_ASSERT(image.width() == int(gmicImage._width));
Q_ASSERT(image.height() == int(gmicImage._height));
Q_ASSERT(image.format() == QImage::Format_ARGB32);
switch (gmicImage._spectrum)
{
case 1:
{
for (int y = 0; y < image.height(); y++)
{
const QRgb *pixel = reinterpret_cast<const QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++)
{
pos = y * gmicImage._width + x;
gmicImage._data[pos] = qGray(pixel[x]) * multiplied;
}
}
break;
}
case 2:
{
for (int y = 0; y < image.height(); y++)
{
const QRgb *pixel = reinterpret_cast<const QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++)
{
pos = y * gmicImage._width + x;
gmicImage._data[pos] = qGray(pixel[x]) * multiplied;
gmicImage._data[pos + greenOffset] = qAlpha(pixel[x]) * multiplied;
}
}
break;
}
case 3:
{
for (int y = 0; y < image.height(); y++)
{
const QRgb *pixel = reinterpret_cast<const QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++)
{
pos = y * gmicImage._width + x;
gmicImage._data[pos] = qRed(pixel[x]) * multiplied;
gmicImage._data[pos + greenOffset] = qGreen(pixel[x]) * multiplied;
gmicImage._data[pos + blueOffset] = qBlue(pixel[x]) * multiplied;
}
}
break;
}
case 4:
{
for (int y = 0; y < image.height(); y++)
{
const QRgb *pixel = reinterpret_cast<const QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); x++)
{
pos = y * gmicImage._width + x;
gmicImage._data[pos] = qRed(pixel[x]) * multiplied;
gmicImage._data[pos + greenOffset] = qGreen(pixel[x]) * multiplied;
gmicImage._data[pos + blueOffset] = qBlue(pixel[x]) * multiplied;
gmicImage._data[pos + alphaOffset] = qAlpha(pixel[x]) * multiplied;
}
}
break;
}
default:
{
Q_ASSERT(false);
- kFatal() << "Unexpected gmic image format";
+ dbgKrita << "Unexpected gmic image format";
break;
}
}
}
diff --git a/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp b/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
index 7be3cd360b9..8c61034c543 100644
--- a/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
+++ b/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
@@ -1,969 +1,969 @@
/*
* Copyright (c) 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <KoColorSpaceRegistry.h>
#include <kis_image.h>
#include <KoColorSpace.h>
#include <KoColorModelStandardIds.h>
#include <KoColor.h>
#include <kis_surrogate_undo_adapter.h>
#include <qtest_kde.h>
#include <QImage>
#include <QTextDocument>
#include <kis_gmic_parser.h>
#include <Component.h>
#include <kis_gmic_filter_model.h>
#include <Command.h>
#include <kis_gmic_simple_convertor.h>
#include <kis_gmic_blacklister.h>
#include <kis_input_output_mapper.h>
#include <kis_gmic_applicator.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <QDomDocument>
#include <kis_group_layer.h>
#include <kis_painter.h>
#include <kis_selection.h>
#include <commands/kis_set_global_selection_command.h>
#include <kis_processing_applicator.h>
#include <testutil.h>
#include "kis_gmic_tests.h"
#include <gmic.h>
#include <Category.h>
#ifndef FILES_DATA_DIR
#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita"
#endif
class FilterDescription
{
public:
QString filterName;
QString category;
QString gmicCommand;
};
using namespace cimg_library;
const static QString EXTENSION = ".png";
const static QString STANDARD_SETTINGS("update1610.gmic");
const static QString BLACKLIST("gmic_def.gmic.blacklist");
void KisGmicTests::initTestCase()
{
KGlobal::dirs()->addResourceType("gmic_definitions", "data", "krita/gmic/");
QString standardSettings("gmic_def.gmic");
QString definitionFilePath = KGlobal::dirs()->findResource("gmic_definitions", STANDARD_SETTINGS);
m_blacklistFilePath = KGlobal::dirs()->findResource("gmic_definitions", STANDARD_SETTINGS + ".blacklist");
QStringList filePaths;
filePaths << definitionFilePath;
KisGmicParser parser(filePaths);
m_root = parser.createFilterTree();
m_blacklistFilePath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", BLACKLIST);
m_blacklister = new KisGmicBlacklister(m_blacklistFilePath);
m_qimage = QImage(QString(FILES_DATA_DIR)+"/"+"poster_rodents_bunnysize.jpg");
m_qimage = m_qimage.convertToFormat(QImage::Format_ARGB32);
m_gmicImage.assign(m_qimage.width(),m_qimage.height(),1,4); // rgba
KisGmicSimpleConvertor::convertFromQImage(m_qimage, m_gmicImage);
m_images.assign(1);
}
void KisGmicTests::cleanupTestCase()
{
const unsigned int zero(0);
m_images.assign(zero);
delete m_blacklister;
delete m_root;
}
#ifdef RUN_FILTERS
void KisGmicTests::testColorizeFilter()
{
QString filterName = "Colorize [comics]";
QString filterCategory = "Black & white";
Command * cmd = KisGmicBlacklister::findFilter(m_root, filterCategory, filterName);
KisGmicFilterSetting filterSettings;
if (cmd == 0)
{
- qDebug() << "Filter not found!";
+ dbgKrita << "Filter not found!";
}else
{
cmd->setParameter("Input layers", "Lineart + color spots");
cmd->setParameter("Output layers", "Lineart + color spots + extrapolated colors");
cmd->setParameter("Smoothness", "0.05");
cmd->writeConfiguration(&filterSettings);
}
QVERIFY(!filterSettings.gmicCommand().isEmpty());
// load 3 layers here
QStringList layerNames;
layerNames << "02_Artline.png";
layerNames << "01_ColorMarks.png";
layerNames << "00_BG.png";
m_images.assign(layerNames.size());
int layerIndex = 0;
for (int i = 0; i < layerNames.size(); i++)
{
const QString& layerName = layerNames.at(i);
QImage layerImage = QImage(QString(FILES_DATA_DIR) + QDir::separator() + layerName).convertToFormat(QImage::Format_ARGB32);
gmic_image<float> gmicImage;
gmicImage.assign(layerImage.width(), layerImage.height(), 1, 4);
KisGmicSimpleConvertor::convertFromQImage(layerImage, gmicImage);
m_images[layerIndex] = gmicImage;
layerIndex++;
}
filterWithGmic(&filterSettings, "multi_colorize", m_images);
}
#endif
#define VERBOSE
typedef QPair<QString, QString> StringPair;
void KisGmicTests::testBlacklisterSearchByParamName()
{
QVector<StringPair> paramFilters;
paramFilters << StringPair("1st additional palette (.gpl file)","Colorize [interactive]");
paramFilters << StringPair("2nd additional palette (.gpl file)","Colorize [interactive]");
paramFilters << StringPair("Clut filename","User-defined");
paramFilters << StringPair("Filename","Import data");
bool beVerbose = false;
#ifdef VERBOSE
beVerbose = true;
#endif
for (int i = 0; i < paramFilters.size(); i++)
{
const QString& paramName = paramFilters.at(i).first;
const QString& expectedFilterName = paramFilters.at(i).second;
if (beVerbose)
{
- qDebug() << "Searching for filter by parameter name :" << paramName;
+ dbgKrita << "Searching for filter by parameter name :" << paramName;
}
QList<Command *> filters = KisGmicBlacklister::findFilterByParamName(m_root, paramName, "file");
if (filters.size() == 0)
{
- qDebug() << "Can't find filter by param name: " << paramName;
+ dbgKrita << "Can't find filter by param name: " << paramName;
}
QCOMPARE(filters.size(), 1);
Command * c = filters.at(0);
QCOMPARE(c->name(), expectedFilterName);
if (beVerbose)
{
- qDebug() << "FilterName: << \"" + c->name() + "\" << \"" + KisGmicBlacklister::toPlainText(c->parent()->name()) + "\"" << " passed!";
+ dbgKrita << "FilterName: << \"" + c->name() + "\" << \"" + KisGmicBlacklister::toPlainText(c->parent()->name()) + "\"" << " passed!";
}
}
}
void KisGmicTests::testFindFilterByPath()
{
Category * path = new Category;
path->setName("<i>About</i>");
Command * cmd = new Command(path);
cmd->setName("Contributors");
path->add(cmd);
Component * c = KisGmicBlacklister::findFilterByPath(m_root, path);
QVERIFY(c != 0);
QVERIFY(c->name() == cmd->name());
delete path;
}
void KisGmicTests::testGatherLayers()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
QImage background(QString(FILES_DATA_DIR) + QDir::separator() + "00_BG.png");
QImage colorMarks(QString(FILES_DATA_DIR) + QDir::separator() + "01_ColorMarks.png");
QImage artLine(QString(FILES_DATA_DIR) + QDir::separator() + "02_Artline.png");
KisImageSP image = new KisImage(0, 2408, 3508, colorSpace, "filter test");
KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
KisPaintDeviceSP device3 = new KisPaintDevice(colorSpace);
device1->convertFromQImage(background, 0, 0, 0);
device2->convertFromQImage(colorMarks, 0, 0, 0);
device3->convertFromQImage(artLine, 0, 0, 0);
KisLayerSP paintLayer1 = new KisPaintLayer(image, "background", OPACITY_OPAQUE_U8, device1);
KisLayerSP paintLayer2 = new KisPaintLayer(image, "colorMarks", OPACITY_OPAQUE_U8, device2);
KisLayerSP paintLayer3 = new KisPaintLayer(image, "artLine", OPACITY_OPAQUE_U8, device3);
image->addNode(paintLayer1, image->rootLayer());
image->addNode(paintLayer2, image->rootLayer());
image->addNode(paintLayer3, image->rootLayer());
KisNodeSP activeNode = static_cast<KisNodeSP>(paintLayer2);
KisNodeListSP result;
KisInputOutputMapper mapper(image, activeNode);
result = mapper.inputNodes(ACTIVE_LAYER);
QCOMPARE(result->at(0)->name(), activeNode->name());
result = mapper.inputNodes(ACTIVE_LAYER_ABOVE_LAYER);
QCOMPARE(result->size(), 2);
QCOMPARE(result->at(0)->name(), activeNode->name());
QCOMPARE(result->at(1)->name(), paintLayer3->name());
result = mapper.inputNodes(ACTIVE_LAYER_BELOW_LAYER);
QCOMPARE(result->size(), 2);
QCOMPARE(result->at(0)->name(), activeNode->name());
QCOMPARE(result->at(1)->name(), paintLayer1->name());
result = mapper.inputNodes(ALL_LAYERS);
QCOMPARE(result->size(), 3);
QCOMPARE(result->at(0)->name(), paintLayer3->name());
QCOMPARE(result->at(1)->name(), paintLayer2->name());
QCOMPARE(result->at(2)->name(), paintLayer1->name());
}
void KisGmicTests::testAllFilters()
{
QQueue<Component *> q;
q.enqueue(m_root);
KisGmicFilterSetting filterSettings;
int filterCount = 0;
int failed = 0;
int success = 0;
QVector<QString> failedFilters;
while (!q.isEmpty())
{
Component * c = q.dequeue();
if (c->childCount() == 0)
{
Command * cmd = static_cast<Command *>(c);
cmd->writeConfiguration(&filterSettings);
- //qDebug() << "Filter: " << c->name() << filterSettings.gmicCommand();
+ //dbgKrita << "Filter: " << c->name() << filterSettings.gmicCommand();
if (!filterSettings.gmicCommand().startsWith("-_none_"))
{
filterCount++;
#ifdef RUN_FILTERS
QString filterName = KisGmicBlacklister::toPlainText(cmd->name());
QString categoryName = KisGmicBlacklister::toPlainText(cmd->parent()->name()); // parent is category
if (isAlreadyThere( filePathify( filterName ) ))
{
- qDebug() << "Already works, skipping filter" << filterName;
+ dbgKrita << "Already works, skipping filter" << filterName;
success++;
}
else if (m_blacklister->isBlacklisted(filterName, categoryName))
{
- qDebug() << "Blacklisted filter, increase fails" << filterName;
+ dbgKrita << "Blacklisted filter, increase fails" << filterName;
failed++;
failedFilters.append(categoryName+":"+filterName);
}
else
{
- qDebug() << "Filtering with:";
- qDebug() << QString("<category name=\"%0\">").arg(categoryName);
- qDebug() << QString("<filter name=\"%0\" />").arg(filterName);
+ dbgKrita << "Filtering with:";
+ dbgKrita << QString("<category name=\"%0\">").arg(categoryName);
+ dbgKrita << QString("<filter name=\"%0\" />").arg(filterName);
// clear previous data?
m_images.assign(1);
// copy?
m_images._data[0] = m_gmicImage;
bool result = filterWithGmic(&filterSettings, filterName, m_images);
result ? success++ : failed++;
- qDebug() << "Progress status:" << "Failed:" << failed << " Success: " << success;
+ dbgKrita << "Progress status:" << "Failed:" << failed << " Success: " << success;
if (result == false)
{
failedFilters.append(categoryName+":"+filterName);
}
}
#endif
}
}
else
{
for (int i=0; i < c->childCount(); i++)
{
q.enqueue(c->child(i));
}
}
}
#ifdef RUN_FILTERS
- qDebug() << "Finish status:" << "Failed:" << failed << " Success: " << success;
- qDebug() << "== Failed filters ==";
+ dbgKrita << "Finish status:" << "Failed:" << failed << " Success: " << success;
+ dbgKrita << "== Failed filters ==";
foreach (const QString &item, failedFilters)
{
- qDebug() << item;
+ dbgKrita << item;
}
#else
Q_UNUSED(success);
Q_UNUSED(failed);
#endif
}
bool KisGmicTests::filterWithGmic(KisGmicFilterSetting* gmicFilterSetting, const QString &filterName, gmic_list<float> &images)
{
QString fileName = filePathify(filterName);
- qDebug() << "Filename for filter " << filterName << " : " << fileName;
+ dbgKrita << "Filename for filter " << filterName << " : " << fileName;
// Second step : Call G'MIC API to process input images.
//------------------------------------------------------
std::fprintf(stderr,"\n- 2st step : Call G'MIC interpreter.\n");
gmic_list<char> images_names;
try
{
QString gmicCmd = "-* 255 ";
gmicCmd.append(gmicFilterSetting->gmicCommand());
gmic(gmicCmd.toLocal8Bit().constData(), images, images_names);
}
// Catch exception, if an error occured in the interpreter.
catch (gmic_exception &e)
{
dbgPlugins << "\n- Error encountered when calling G'MIC : '%s'\n" << e.what();
return false;
}
// Third step : get back modified image data.
//-------------------------------------------
std::fprintf(stderr,"\n- 3st step : Returned %u output images.\n", images._width);
for (unsigned int i = 0; i < images._width; ++i)
{
std::fprintf(stderr," Output image %u = %ux%ux%ux%u, buffer : %p\n",i,
images._data[i]._width,
images._data[i]._height,
images._data[i]._depth,
images._data[i]._spectrum,
images._data[i]._data);
}
// Forth step : convert to QImage and save
for (unsigned int i = 0; i < images._width; ++i)
{
KisGmicSimpleConvertor convertor;
KisPaintDeviceSP device = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
convertor.convertFromGmicImage(images._data[i], device, 255.0f);
QImage result = device->convertToQImage(0, 0,0,images._data[i]._width, images._data[i]._height);
QString indexString("_%1");
if (images._width > 1)
{
indexString = indexString.arg(i,4, 10, QLatin1Char('_'));
}else
{
indexString = QString();
}
QString fullFileName(QString(FILES_OUTPUT_DIR) + QDir::separator() + fileName + indexString + ".png");
QFileInfo info(fullFileName);
if (info.exists())
{
continue;
}
bool isSaved = result.save(fullFileName);
if (!isSaved)
{
- qDebug() << "Saving " << fileName << "failed";
+ dbgKrita << "Saving " << fileName << "failed";
return false;
}else
{
- qDebug() << "Saved " << fullFileName << " -- OK";
+ dbgKrita << "Saved " << fullFileName << " -- OK";
}
}
return true;
}
QString KisGmicTests::filePathify(const QString& fileName)
{
QStringList illegalCharacters;
illegalCharacters << QLatin1String("/")
<< QLatin1String("\\")
<< QLatin1String("?")
<< QLatin1String(":");
QString result = fileName;
foreach(const QString& item, illegalCharacters)
{
result = result.replace(item ,"_");
}
return result;
}
void KisGmicTests::testBlacklister()
{
QString category("Colors");
QString filterName("User-defined");
QVERIFY(m_blacklister->isBlacklisted(filterName, category));
}
bool KisGmicTests::isAlreadyThere(QString fileName)
{
QString path = QString(FILES_OUTPUT_DIR) + QDir::separator() + fileName + EXTENSION;
QFileInfo info(path);
return info.exists();
}
void KisGmicTests::testConvertGrayScaleGmic()
{
KisPaintDeviceSP resultDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP resultDevFast = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
gmic_image<float> gmicImage;
gmicImage.assign(m_qimage.width(),m_qimage.height(),1,1);
KisGmicSimpleConvertor::convertFromQImage(m_qimage, gmicImage, 1.0);
KisGmicSimpleConvertor::convertFromGmicImage(gmicImage, resultDev, 1.0);
KisGmicSimpleConvertor::convertFromGmicFast(gmicImage, resultDevFast, 1.0);
QImage slowQImage = resultDev->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
QImage fastQImage = resultDevFast->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint,slowQImage,fastQImage))
{
QFAIL(QString("Slow method produces different result then fast to convert gmic grayscale pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
slowQImage.save("grayscale.bmp");
fastQImage.save("grayscale_fast.bmp");
}
}
void KisGmicTests::testConvertGrayScaleAlphaGmic()
{
KisPaintDeviceSP resultDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP resultDevFast = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
gmic_image<float> gmicImage;
gmicImage.assign(m_qimage.width(),m_qimage.height(),1,2);
KisGmicSimpleConvertor::convertFromQImage(m_qimage, gmicImage, 1.0);
KisGmicSimpleConvertor::convertFromGmicImage(gmicImage, resultDev, 1.0);
KisGmicSimpleConvertor::convertFromGmicFast(gmicImage, resultDevFast, 1.0);
QImage slowQImage = resultDev->convertToQImage(0, 0,0,gmicImage._width, gmicImage._height);
QImage fastQImage = resultDevFast->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint,slowQImage,fastQImage))
{
QFAIL(QString("Slow method produces different result then fast to convert gmic grayscale pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
slowQImage.save("grayscale.bmp");
fastQImage.save("grayscale_fast.bmp");
}
}
void KisGmicTests::testConvertRGBgmic()
{
KisPaintDeviceSP resultDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP resultDevFast = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
gmic_image<float> gmicImage;
gmicImage.assign(m_qimage.width(),m_qimage.height(),1,3);
KisGmicSimpleConvertor::convertFromQImage(m_qimage, gmicImage, 1.0);
KisGmicSimpleConvertor::convertFromGmicImage(gmicImage, resultDev, 1.0);
KisGmicSimpleConvertor::convertFromGmicFast(gmicImage, resultDevFast, 1.0);
QImage slowQImage = resultDev->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint,slowQImage,m_qimage))
{
QFAIL(QString("Slow method failed to convert gmic RGB pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
slowQImage.save("RGB.bmp");
}
QImage fastQImage = resultDevFast->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
if (!TestUtil::compareQImages(errpoint,fastQImage,m_qimage))
{
QFAIL(QString("Fast method failed to convert gmic RGB pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
fastQImage.save("RGB_fast.bmp");
}
}
void KisGmicTests::testConvertRGBAgmic()
{
KisPaintDeviceSP resultDev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
KisPaintDeviceSP resultDevFast = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
gmic_image<float> gmicImage;
gmicImage.assign(m_qimage.width(),m_qimage.height(),1,4);
KisGmicSimpleConvertor::convertFromQImage(m_qimage, gmicImage, 1.0);
KisGmicSimpleConvertor::convertFromGmicImage(gmicImage, resultDev, 1.0);
KisGmicSimpleConvertor::convertFromGmicFast(gmicImage, resultDevFast, 1.0);
QImage slowQImage = resultDev->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
QPoint errpoint;
if (!TestUtil::compareQImages(errpoint,slowQImage,m_qimage))
{
QFAIL(QString("Slow method failed to convert gmic RGBA pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
slowQImage.save("RGBA.bmp");
}
QImage fastQImage = resultDevFast->convertToQImage(0,0,0,gmicImage._width, gmicImage._height);
if (!TestUtil::compareQImages(errpoint,fastQImage,m_qimage))
{
QFAIL(QString("Fast method failed to convert gmic RGBA pixel format, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
fastQImage.save("RGBA_fast.bmp");
}
}
void KisGmicTests::testFilterOnlySelection()
{
const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();
int width = 3840;
int height = 2400;
KisImageSP image = new KisImage(undoStore, width, height, colorSpace, "filter selection test");
QImage simpleImage(QString(FILES_DATA_DIR) + QDir::separator() + "poster_rodents_bunnysize.jpg");
KisPaintDeviceSP device = new KisPaintDevice(colorSpace);
device->convertFromQImage(simpleImage, 0, 0, 0);
KisLayerSP paintLayer = new KisPaintLayer(image, "background", OPACITY_OPAQUE_U8, device);
image->addNode(paintLayer, image->rootLayer());
// add global selection
QPoint topLeft(0.25 * width, 0.25 * height);
QPoint bottomRight(0.75 * width, 0.75 * height);
QRect selectionRect(topLeft, bottomRight);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(0, image));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
pixelSelection->select(selectionRect);
KUndo2Command *cmd = new KisSetGlobalSelectionCommand(image, selection);
image->undoAdapter()->addCommand(cmd);
QRect selected = image->globalSelection()->selectedExactRect();
QCOMPARE(selectionRect, selected);
// filter kis image with gmic
KisNodeListSP kritaNodes(new QList< KisNodeSP >());
kritaNodes->append(static_cast<KisNodeSP>(paintLayer));
// select some simple filter
QString filterName = "Sepia";
QString filterCategory = "Colors";
Command * gmicCmd = KisGmicBlacklister::findFilter(m_root, filterCategory, filterName);
KisGmicFilterSetting filterSettings;
if (gmicCmd == 0)
{
- qDebug() << "Filter not found!";
+ dbgKrita << "Filter not found!";
}
else
{
gmicCmd->writeConfiguration(&filterSettings);
}
QVERIFY(!filterSettings.gmicCommand().isEmpty());
QString gmicCommand = filterSettings.gmicCommand();
KisGmicApplicator applicator;
applicator.setProperties(image, image->root(), kundo2_noi18n("Gmic filter"), kritaNodes, gmicCommand);
applicator.preview();
applicator.finish();
image->waitForDone();
//image->convertToQImage(image->bounds(), 0).save("filteredSelection.png");
//TODO: check with expected image!
}
void KisGmicTests::testLoadingGmicCommands()
{
QString definitionFilePath = KGlobal::dirs()->findResource("gmic_definitions", STANDARD_SETTINGS);
QByteArray data = KisGmicParser::extractGmicCommandsOnly(definitionFilePath);
QVERIFY(data.size() > 0);
}
void KisGmicTests::generateXmlDump(const QString &outputFilePath)
{
QDomDocument doc = KisGmicBlacklister::dumpFiltersToXML(m_root);
QFile outputFile(outputFilePath);
if (outputFile.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream ts(&outputFile);
ts << doc.toString();
outputFile.close();
}
}
void KisGmicTests::testCompareToGmicGimp()
{
QVector<FilterDescription> filterDescriptions;
QDomDocument doc("myDoc");
QFile file(QString(FILES_DATA_DIR)+"/"+"gmic_def_1610_gimp.xml");
QVERIFY(file.open(QIODevice::ReadOnly));
QVERIFY(doc.setContent(&file));
QDomNodeList children = doc.documentElement().childNodes();
for (int i = 0; i < children.size(); i++)
{
if (children.at(i).isElement())
{
QDomElement elem = children.at(i).toElement();
QDomElement cmdElem = elem.firstChildElement("gmicCommand");
QVERIFY(!cmdElem.isNull());
FilterDescription fd;
fd.filterName = elem.attribute("name");
fd.category = QString();
fd.gmicCommand = cmdElem.text();
// symbolic home to real directory
fd.gmicCommand = fd.gmicCommand.replace("[[:home:]]", QDir::homePath());
fd.gmicCommand = fd.gmicCommand.replace("\n", "\\n");
filterDescriptions.append(fd);
}
}
verifyFilters(filterDescriptions);
}
typedef QPair<Component *, QString> FilterPair;
void KisGmicTests::testCompareToKrita()
{
QString fileName = "gmic_def_" + QString::number(int(gmic_version)) + "_krita.xml";
QString filePath = QString(FILES_DATA_DIR)+"/"+fileName;
QFileInfo info(filePath);
if (!info.exists())
{
- qWarning() << "Reference xml file for the krita parser does not exist, creating one!";
- qWarning() << "Creating it at " << filePath;
+ warnKrita << "Reference xml file for the krita parser does not exist, creating one!";
+ warnKrita << "Creating it at " << filePath;
generateXmlDump(filePath);
}
QFile file(filePath);
QVERIFY(file.open(QIODevice::ReadOnly));
// Load the xml file that Krita produced
// select filter, category, command
QDomDocument doc("mydocument");
QVERIFY(doc.setContent(&file));
QVector<FilterPair> filterDescriptions;
QDomElement child = doc.documentElement().firstChildElement();
QQueue<QDomElement> queue;
queue.enqueue(child);
while (!queue.isEmpty())
{
QDomElement elem = queue.dequeue();
if (elem.tagName() == "filter")
{
if (elem.parentNode().isElement())
{
QDomElement cmdElem = elem.firstChildElement("gmicCommand");
QVERIFY(!cmdElem.isNull());
QString gmicCommand = cmdElem.text().replace("[[:home:]]", QDir::homePath());
Command * command = new Command;
command->setName(elem.attribute("name"));
QDomElement iter = elem.parentNode().toElement();
Category * currentCategory = 0;
while (iter.isElement())
{
if (iter.tagName() == "category")
{
if (iter.attribute("name") == "Filters")
{
break;
}
Category * category = new Category;
category->setName(iter.attribute("name"));
if (currentCategory == 0)
{
category->add(command);
command->setParent(category);
}
else
{
category->add(currentCategory);
currentCategory->setParent(category);
}
currentCategory = category;
}
else
{
- qWarning() << "Unexpected element #20151819 " << iter.tagName();
+ warnKrita << "Unexpected element #20151819 " << iter.tagName();
}
iter = iter.parentNode().toElement();
}
FilterPair pair = FilterPair(currentCategory, gmicCommand);
filterDescriptions.append(pair);
}
}
else
{
QDomNodeList children = elem.childNodes();
for (int i = 0; i < children.size(); i++)
{
if (children.at(i).isElement())
{
queue.enqueue(children.at(i).toElement());
}
}
}
}
//verifyFilters(filterDescriptions);
int success = 0;
for (int i = 0; i < filterDescriptions.size(); i++)
{
Component * path = filterDescriptions.at(i).first;
const QString &gmicCommand = filterDescriptions.at(i).second;
Component * component = KisGmicBlacklister::findFilterByPath(m_root, path);
if (!component)
{
qDebug () << "Can't find filter: " << pathToString(path);
}
else
{
KisGmicFilterSetting filterSettings;
static_cast<Command *>(component)->writeConfiguration(&filterSettings);
if (filterSettings.gmicCommand() != gmicCommand)
{
- qDebug() << "Filter : " << pathToString(path);
- qDebug() << " Actual: " << filterSettings.gmicCommand();
- qDebug() << "Expected: " << gmicCommand;
+ dbgKrita << "Filter : " << pathToString(path);
+ dbgKrita << " Actual: " << filterSettings.gmicCommand();
+ dbgKrita << "Expected: " << gmicCommand;
}
else
{
success++;
}
}
}
int count = filterDescriptions.size();
if (success != count)
{
- qDebug() << "Number of failed filters: " << count - success;
+ dbgKrita << "Number of failed filters: " << count - success;
}
QCOMPARE(success, count);
for (int i = 0; i < filterDescriptions.size(); i++)
{
delete filterDescriptions[i].first;
}
}
QString KisGmicTests::pathToString(Component* c)
{
QStringList pathItems;
Component * iter = c;
while (iter->childCount() > 0)
{
pathItems << iter->name();
iter = iter->child(0);
}
pathItems << iter->name();
return pathItems.join(" / ");
}
void KisGmicTests::verifyFilters(QVector< FilterDescription > filters)
{
// without knowing the category (the dumped xml file for gmic gimp does not contain
// this information, because it is non-trivial to extract it automatically)
// we will try to find matching filters
int count = filters.size();
// Expected failures due to buggy filter definitions
QSet<QString> expectedFilterFailures;
expectedFilterFailures
<< "Ellipsionism" // MINOR: buggy filter definition: default value bigger than max
<< "Simple local contrast" // TODO: bug in our parser
<< "Granular texture" // MINOR
<< "Repair scanned document" // MINOR
<< "Cartoon [animated]" // MINOR
<< "3d video conversion" // TODO: bug in our parser
<< "Luminance to alpha" //buggy filter definition
<< "Generate film data" // buggy filter definition (folder param)
<< "Motifs degrades cie"; // buggy filter definition (default value bigger than max)
int success = 0;
int expectedFail = 0;
for (int i = 0; i < count; i++)
{
FilterDescription fd = filters.at(i);
QVector<Command *> commands;
if (fd.category.isEmpty())
{
commands = KisGmicBlacklister::filtersByName(m_root, fd.filterName);
if (commands.size() == 0)
{
- qWarning() << "Can't find "<< "filterName: " << fd.filterName;
+ warnKrita << "Can't find "<< "filterName: " << fd.filterName;
continue;
}
#if 0
if (commands.size() > 1)
{
- qDebug() << "Multiple entries found:";
+ dbgKrita << "Multiple entries found:";
foreach (Command * c, commands)
{
- qDebug() << "filter: " << c->name() << "category: " << c->parent()->name();
+ dbgKrita << "filter: " << c->name() << "category: " << c->parent()->name();
}
}
#endif
}
else
{
Command * c = KisGmicBlacklister::findFilter(m_root, fd.category, fd.filterName);
if (c == 0)
{
- qWarning() << "Can't find "<< "filterName: " << fd.filterName << "category: " << fd.category;
+ warnKrita << "Can't find "<< "filterName: " << fd.filterName << "category: " << fd.category;
continue;
}else
{
commands.append(c);
}
}
bool foundMatch = false;
QString lastGmicCommand;
QString errors;
for (int i = 0; i < commands.size(); i++)
{
QTextStream errorLog(&errors);
Component * c = commands[i];
QVERIFY(c->childCount() == 0);
Command * cmd = dynamic_cast<Command *>(c);
QVERIFY(cmd != 0);
KisGmicFilterSetting filterSettings;
cmd->writeConfiguration(&filterSettings);
lastGmicCommand = filterSettings.gmicCommand();
if (filterSettings.gmicCommand() == fd.gmicCommand)
{
foundMatch = true;
}
}
if (foundMatch)
{
success++;
}
else
{
if (expectedFilterFailures.contains(fd.filterName))
{
expectedFail++;
}
else
{
- qDebug() << "Filter " << fd.category << " / " << fd.filterName << " does not match any of " << commands.size() << " filter definitions!";
- qDebug() << "Expected: " << fd.gmicCommand;
+ dbgKrita << "Filter " << fd.category << " / " << fd.filterName << " does not match any of " << commands.size() << " filter definitions!";
+ dbgKrita << "Expected: " << fd.gmicCommand;
if (commands.size() == 1)
{
- qDebug() << " Actual: " << lastGmicCommand;
+ dbgKrita << " Actual: " << lastGmicCommand;
}
else
{
- qDebug() << "=== BEGIN ==";
- qDebug() << errors;
- qDebug() << "=== END ===";
+ dbgKrita << "=== BEGIN ==";
+ dbgKrita << errors;
+ dbgKrita << "=== END ===";
}
}
}
}
if (success != count)
{
- qDebug() << "=== Stats ===";
- qDebug() << "Number of failed filters (with expected failures): " << count - success;
- qDebug() << "Number of expected failures:" << expectedFail;
+ dbgKrita << "=== Stats ===";
+ dbgKrita << "Number of failed filters (with expected failures): " << count - success;
+ dbgKrita << "Number of expected failures:" << expectedFail;
}
int realSuccess = success + expectedFail;
QCOMPARE(realSuccess,count);
}
void KisGmicTests::testFindFilterByName()
{
QVector<Command *> commands = KisGmicBlacklister::filtersByName(m_root, "B&W stencil");
QVERIFY(commands.size() > 0);
}
QTEST_KDEMAIN(KisGmicTests, NoGUI)
diff --git a/krita/plugins/extensions/imagesplit/imagesplit.cpp b/krita/plugins/extensions/imagesplit/imagesplit.cpp
index 524f94a6ec1..730be9d8a17 100644
--- a/krita/plugins/extensions/imagesplit/imagesplit.cpp
+++ b/krita/plugins/extensions/imagesplit/imagesplit.cpp
@@ -1,162 +1,162 @@
/*
* imagesplit.cc -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "imagesplit.h"
#include <QStringList>
#include <QDir>
#include <QDesktopServices>
#include <klocale.h>
#include <kpluginfactory.h>
#include <kmimetype.h>
#include <KisImportExportManager.h>
#include <KoFileDialog.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <kis_debug.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_action.h>
#include <KisDocument.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include "dlg_imagesplit.h"
K_PLUGIN_FACTORY_WITH_JSON(ImagesplitFactory, "kritaimagesplit.json", registerPlugin<Imagesplit>();)
Imagesplit::Imagesplit(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = new KisAction(i18n("Image Split "), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
addAction("imagesplit", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImagesplit()));
}
Imagesplit::~Imagesplit()
{
}
void Imagesplit::saveAsImage(QRect imgSize, QString mimeType, KUrl url)
{
KisImageWSP image = m_view->image();
KisDocument *d = KisPart::instance()->createDocument();
d->prepareForImport();
KisImageWSP dst = new KisImage(d->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName());
dst->setResolution(image->xRes(), image->yRes());
d->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, dst->nextLayerName(), 255);
KisPainter gc(paintLayer->paintDevice());
gc.bitBlt(QPoint(0, 0), image->projection(), imgSize);
dst->addNode(paintLayer, KisNodeSP(0));
dst->refreshGraph();
d->setOutputMimeType(mimeType.toLatin1());
d->exportDocument(url);
delete d;
}
void Imagesplit::slotImagesplit()
{
// Taking the title - url from caption function and removing file extension
QStringList strList = ((m_view->document())->caption()).split('.');
QString suffix = strList.at(0);
// Getting all mime types and converting them into names which are displayed at combo box
QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export);
QStringList filteredMimeTypes;
QStringList listFileType;
foreach(const QString & tempStr, listMimeFilter) {
KMimeType::Ptr type = KMimeType::mimeType(tempStr);
- qDebug() << tempStr << type;
+ dbgKrita << tempStr << type;
if (type) {
listFileType.append(type->comment());
filteredMimeTypes.append(tempStr);
}
}
listMimeFilter = filteredMimeTypes;
Q_ASSERT(listMimeFilter.size() == listFileType.size());
DlgImagesplit * dlgImagesplit = new DlgImagesplit(m_view, suffix, listFileType);
dlgImagesplit->setObjectName("Imagesplit");
Q_CHECK_PTR(dlgImagesplit);
KisImageWSP image = m_view->image();
if (dlgImagesplit->exec() == QDialog::Accepted) {
int numHorizontalLines = dlgImagesplit->horizontalLines();
int numVerticalLines = dlgImagesplit->verticalLines();
int img_width = image->width() / (numVerticalLines + 1);
int img_height = image->height() / (numHorizontalLines + 1);
if (dlgImagesplit->autoSave()) {
for (int i = 0, k = 1; i < (numVerticalLines + 1); i++) {
for (int j = 0; j < (numHorizontalLines + 1); j++, k++) {
KMimeType::Ptr mimeTypeSelected = KMimeType::mimeType(listMimeFilter.at(dlgImagesplit->cmbIndex));
KUrl url(QDir::homePath());
QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + mimeTypeSelected->mainExtension();
url.addPath(fileName);
KUrl kurl = url.url();
saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), listMimeFilter.at(dlgImagesplit->cmbIndex), kurl);
}
}
}
else {
for (int i = 0; i < (numVerticalLines + 1); i++) {
for (int j = 0; j < (numHorizontalLines + 1); j++) {
KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::SaveFile, "OpenDocument");
dialog.setCaption(i18n("Save Image on Split"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(listMimeFilter);
KUrl url = dialog.url();
KMimeType::Ptr mime = KMimeType::findByUrl(url);
QString mimefilter = mime->name();
if (url.isEmpty())
return;
saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), mimefilter, url);
}
}
}
}
delete dlgImagesplit;
}
#include "imagesplit.moc"
diff --git a/krita/plugins/extensions/imagesplit/wdg_imagesplit.cpp b/krita/plugins/extensions/imagesplit/wdg_imagesplit.cpp
index dfc439e20aa..9cfc566dd28 100644
--- a/krita/plugins/extensions/imagesplit/wdg_imagesplit.cpp
+++ b/krita/plugins/extensions/imagesplit/wdg_imagesplit.cpp
@@ -1,47 +1,47 @@
/*
* dlg_imagesplit.cc - part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "wdg_imagesplit.h"
#include <QPainter>
#include <QStringList>
#include <QImage>
#include <QListWidgetItem>
-#include <QDebug>
+#include <kis_debug.h>
#include "kis_factory2.h"
#include "kis_config.h"
WdgImagesplit::WdgImagesplit(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
KisConfig cfg;
intHorizontalSplitLines->setValue(cfg.horizontalSplitLines());
intVerticalSplitLines->setValue(cfg.verticalSplitLines());
chkAutoSave->setChecked(true);
}
diff --git a/krita/plugins/extensions/layersplit/layersplit.cpp b/krita/plugins/extensions/layersplit/layersplit.cpp
index 084663bd3b9..2f47cb67918 100644
--- a/krita/plugins/extensions/layersplit/layersplit.cpp
+++ b/krita/plugins/extensions/layersplit/layersplit.cpp
@@ -1,221 +1,221 @@
/*
* Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "layersplit.h"
#include <QMap>
#include <QPointer>
#include <QHash>
#include <klocale.h>
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <kis_debug.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_action.h>
#include <KisDocument.h>
#include <kis_node.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_random_accessor_ng.h>
#include "dlg_layersplit.h"
#include "kis_node_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_undo_adapter.h"
#include <KoUpdater.h>
#include <KoProgressUpdater.h>
K_PLUGIN_FACTORY_WITH_JSON(LayerSplitFactory, "kritalayersplit.json", registerPlugin<LayerSplit>();)
LayerSplit::LayerSplit(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = new KisAction(i18n("Split Layer..."), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
addAction("layersplit", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSplit()));
}
LayerSplit::~LayerSplit()
{
}
struct Layer {
KoColor color;
KisPaintDeviceSP device;
KisRandomAccessorSP accessor;
int pixelsWritten;
bool operator<(const Layer& other) const
{
return pixelsWritten < other.pixelsWritten;
}
};
void LayerSplit::slotLayerSplit()
{
DlgLayerSplit dlg;
if (dlg.exec() == QDialog::Accepted) {
dlg.hide();
QApplication::setOverrideCursor(Qt::WaitCursor);
KoProgressUpdater* pu = m_view->createProgressUpdater(KoProgressUpdater::Unthreaded);
pu->start(100, i18n("Split into Layers"));
QPointer<KoUpdater> updater = pu->startSubtask();
KisImageSP image = m_view->image();
if (!image) return;
image->lock();
KisNodeSP node = m_view->activeNode();
if (!node) return;
KisPaintDeviceSP projection = node->projection();
if (!projection) return;
QList<Layer> colorMap;
const KoColorSpace *cs = projection->colorSpace();
QRect rc = image->bounds();
int fuzziness = dlg.fuzziness();
updater->setProgress(0);
KisRandomConstAccessorSP acc = projection->createRandomConstAccessorNG(rc.x(), rc.y());
for (int row = rc.y(); row < rc.height(); ++row) {
for (int col = rc.x(); col < rc.width(); ++col) {
acc->moveTo(col, row);
KoColor c(cs);
c.setColor(acc->rawDataConst(), cs);
if (c.opacityU8() == OPACITY_TRANSPARENT_U8) {
continue;
}
if (dlg.disregardOpacity()) {
c.setOpacity(OPACITY_OPAQUE_U8);
}
bool found = false;
foreach(const Layer &l, colorMap) {
if (fuzziness == 0) {
found = (l.color == c);
}
else {
quint8 match = cs->difference(l.color.data(), c.data());
found = (match <= fuzziness);
}
if (found) {
KisRandomAccessorSP dstAcc = l.accessor;
dstAcc->moveTo(col, row);
memcpy(dstAcc->rawData(), acc->rawDataConst(), cs->pixelSize());
const_cast<Layer*>(&l)->pixelsWritten++;
break;
}
}
if (!found) {
Layer l;
l.color = c;
l.device = new KisPaintDevice(cs, KoColor::toQString(c));
l.accessor = l.device->createRandomAccessorNG(col, row);
l.accessor->moveTo(col, row);
memcpy(l.accessor->rawData(), acc->rawDataConst(), cs->pixelSize());
l.pixelsWritten = 1;
colorMap << l;
}
}
if (updater->interrupted()) {
return;
}
updater->setProgress((row - rc.y()) * 100 / rc.height() - rc.y());
}
updater->setProgress(100);
- qDebug() << "Created" << colorMap.size() << "layers";
+ dbgKrita << "Created" << colorMap.size() << "layers";
// foreach(const Layer &l, colorMap) {
-// qDebug() << "\t" << l.device->objectName() << ":" << l.pixelsWritten;
+// dbgKrita << "\t" << l.device->objectName() << ":" << l.pixelsWritten;
// }
if (dlg.sortLayers()) {
qSort(colorMap);
}
KisUndoAdapter *undo = image->undoAdapter();
undo->beginMacro(kundo2_i18n("Split Layer"));
KisNodeCommandsAdapter adapter(m_view);
KisGroupLayerSP baseGroup = dynamic_cast<KisGroupLayer*>(node->parent().data());
if (!baseGroup) {
// Masks are never nested
baseGroup = dynamic_cast<KisGroupLayer*>(node->parent()->parent().data());
}
if (dlg.hideOriginal()) {
node->setVisible(false);
}
if (dlg.createBaseGroup()) {
KisGroupLayerSP grp = new KisGroupLayer(image, i18n("Color"), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
baseGroup = grp;
}
foreach(const Layer &l, colorMap) {
KisGroupLayerSP grp = baseGroup;
if (dlg.createSeparateGroups()) {
grp = new KisGroupLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
}
KisPaintLayerSP paintLayer = new KisPaintLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8, l.device);
adapter.addNode(paintLayer, grp, 0);
paintLayer->setAlphaLocked(dlg.lockAlpha());
}
undo->endMacro();
image->unlock();
image->setModified();
}
QApplication::restoreOverrideCursor();
}
#include "layersplit.moc"
diff --git a/krita/plugins/extensions/layersplit/wdg_layersplit.cpp b/krita/plugins/extensions/layersplit/wdg_layersplit.cpp
index 4937f34c0d0..39d7611a933 100644
--- a/krita/plugins/extensions/layersplit/wdg_layersplit.cpp
+++ b/krita/plugins/extensions/layersplit/wdg_layersplit.cpp
@@ -1,38 +1,38 @@
/*
* Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "wdg_layersplit.h"
#include <QPainter>
#include <QStringList>
#include <QImage>
#include <QListWidgetItem>
-#include <QDebug>
+#include <kis_debug.h>
#include "kis_factory2.h"
#include "kis_config.h"
WdgLayerSplit::WdgLayerSplit(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
}
diff --git a/krita/plugins/extensions/offsetimage/offsetimage.cpp b/krita/plugins/extensions/offsetimage/offsetimage.cpp
index 8717f60a1ac..13ad330c932 100644
--- a/krita/plugins/extensions/offsetimage/offsetimage.cpp
+++ b/krita/plugins/extensions/offsetimage/offsetimage.cpp
@@ -1,146 +1,146 @@
/*
* Copyright (c) 2013 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "offsetimage.h"
#include <cmath>
#include <klocale.h>
#include <kis_debug.h>
#include <kpluginfactory.h>
#include <KoIcon.h>
#include <kis_image.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image_manager.h>
#include <kis_node_manager.h>
#include <kis_canvas_resource_provider.h>
#include <kis_group_layer.h>
#include <kis_image_signal_router.h>
#include <kis_processing_applicator.h>
#include <kis_action.h>
#include <kis_selection.h>
#include "dlg_offsetimage.h"
#include "kis_offset_processing_visitor.h"
K_PLUGIN_FACTORY_WITH_JSON(OffsetImageFactory, "kritaoffsetimage.json", registerPlugin<OffsetImage>();)
OffsetImage::OffsetImage(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
{
KisAction *action = new KisAction(i18n("&Offset Image..."), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
addAction("offsetimage", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotOffsetImage()));
action = new KisAction(i18n("&Offset Layer..."), this);
action->setActivationFlags(KisAction::ACTIVE_LAYER);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
addAction("offsetlayer", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotOffsetLayer()));
}
OffsetImage::~OffsetImage()
{
}
void OffsetImage::slotOffsetImage()
{
KisImageWSP image = m_view->image();
if (image) {
DlgOffsetImage * dlgOffsetImage = new DlgOffsetImage(m_view->mainWindow(), "OffsetImage", offsetWrapRect().size());
Q_CHECK_PTR(dlgOffsetImage);
KUndo2MagicString actionName = kundo2_i18n("Offset Image");
dlgOffsetImage->setCaption(i18nc("@title:window", "Offset Image"));
if (dlgOffsetImage->exec() == QDialog::Accepted) {
QPoint offsetPoint = QPoint(dlgOffsetImage->offsetX(), dlgOffsetImage->offsetY());
offsetImpl(actionName, image->root(), offsetPoint);
}
delete dlgOffsetImage;
}
else
{
- kWarning() << "KisImage not available";
+ dbgKrita << "KisImage not available";
}
}
void OffsetImage::slotOffsetLayer()
{
KisImageWSP image = m_view->image();
if (image) {
DlgOffsetImage * dlgOffsetImage = new DlgOffsetImage(m_view->mainWindow(), "OffsetLayer", offsetWrapRect().size());
Q_CHECK_PTR(dlgOffsetImage);
KUndo2MagicString actionName = kundo2_i18n("Offset Layer");
dlgOffsetImage->setCaption(i18nc("@title:window", "Offset Layer"));
if (dlgOffsetImage->exec() == QDialog::Accepted) {
QPoint offsetPoint = QPoint(dlgOffsetImage->offsetX(), dlgOffsetImage->offsetY());
KisNodeSP activeNode = m_view->activeNode();
offsetImpl(actionName, activeNode, offsetPoint);
}
delete dlgOffsetImage;
}
else
{
- kWarning() << "KisImage not available";
+ dbgKrita << "KisImage not available";
}
}
void OffsetImage::offsetImpl(const KUndo2MagicString& actionName, KisNodeSP node, const QPoint& offsetPoint)
{
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(m_view->image(), node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
QRect rc = offsetWrapRect();
KisProcessingVisitorSP visitor = new KisOffsetProcessingVisitor(offsetPoint, rc);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
}
QRect OffsetImage::offsetWrapRect()
{
QRect offsetWrapRect;
if (m_view->selection())
{
offsetWrapRect = m_view->selection()->selectedExactRect();
}
else
{
offsetWrapRect = m_view->image()->bounds();
}
return offsetWrapRect;
}
#include "offsetimage.moc"
diff --git a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
index 292925bc926..4294a42efab 100644
--- a/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
+++ b/krita/plugins/extensions/resourcemanager/dlg_create_bundle.cpp
@@ -1,387 +1,387 @@
/*
* Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "dlg_create_bundle.h"
#include "ui_wdgdlgcreatebundle.h"
#include <QProcessEnvironment>
#include <QFileInfo>
#include <QMessageBox>
#include <QDesktopServices>
#include <QGridLayout>
#include <QTableWidget>
#include <QPainter>
#include <KisImportExportManager.h>
#include <KoDocumentInfo.h>
#include <KoFileDialog.h>
#include <KoIcon.h>
#include <KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <kis_resource_server_provider.h>
#include <kis_workspace_resource.h>
#include <kis_paintop_preset.h>
#include <kis_brush_server.h>
#include <kis_config.h>
#include "resourcebundle.h"
#define ICON_SIZE 48
DlgCreateBundle::DlgCreateBundle(ResourceBundle *bundle, QWidget *parent)
: KDialog(parent)
, m_ui(new Ui::WdgDlgCreateBundle)
, m_bundle(bundle)
{
m_page = new QWidget();
m_ui->setupUi(m_page);
setMainWidget(m_page);
setFixedSize(m_page->sizeHint());
setButtons(Ok | Cancel);
setDefaultButton(Ok);
setButtonText(Ok, i18n("Save"));
connect(m_ui->bnSelectSaveLocation, SIGNAL(clicked()), SLOT(selectSaveLocation()));
KoDocumentInfo info;
info.updateParameters();
if (bundle) {
setCaption(i18n("Edit Resource Bundle"));
m_ui->lblSaveLocation->setText(QFileInfo(bundle->filename()).absolutePath());
m_ui->editBundleName->setText(bundle->name());
m_ui->editAuthor->setText(bundle->getMeta("author"));
m_ui->editEmail->setText(bundle->getMeta("email"));
m_ui->editLicense->setText(bundle->getMeta("license"));
m_ui->editWebsite->setText(bundle->getMeta("website"));
m_ui->editDescription->document()->setPlainText(bundle->getMeta("description"));
m_ui->lblPreview->setPixmap(QPixmap::fromImage(bundle->image().scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
}
else {
setCaption(i18n("Create Resource Bundle"));
KisConfig cfg;
m_ui->editAuthor->setText(cfg.readEntry<QString>("BundleAuthorName", info.authorInfo("creator")));
m_ui->editEmail->setText(cfg.readEntry<QString>("BundleAuthorEmail", info.authorInfo("email")));
m_ui->editWebsite->setText(cfg.readEntry<QString>("BundleWebsite", "http://"));
m_ui->editLicense->setText(cfg.readEntry<QString>("BundleLicense", "CC-BY-SA"));
m_ui->lblSaveLocation->setText(cfg.readEntry<QString>("BundleExportLocation", QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)));
}
m_ui->bnAdd->setIcon(themedIcon("arrow-right"));
connect(m_ui->bnAdd, SIGNAL(clicked()), SLOT(addSelected()));
m_ui->bnRemove->setIcon(themedIcon("arrow-left"));
connect(m_ui->bnRemove, SIGNAL(clicked()), SLOT(removeSelected()));
m_ui->cmbResourceTypes->addItem(i18n("Brushes"), QString("brushes"));
m_ui->cmbResourceTypes->addItem(i18n("Brush Presets"), QString("presets"));
m_ui->cmbResourceTypes->addItem(i18n("Gradients"), QString("gradients"));
m_ui->cmbResourceTypes->addItem(i18n("Patterns"), QString("patterns"));
m_ui->cmbResourceTypes->addItem(i18n("Palettes"), QString("palettes"));
m_ui->cmbResourceTypes->addItem(i18n("Workspaces"), QString("workspaces"));
connect(m_ui->cmbResourceTypes, SIGNAL(activated(int)), SLOT(resourceTypeSelected(int)));
m_ui->tableAvailable->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
m_ui->tableAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->tableSelected->setIconSize(QSize(ICON_SIZE, ICON_SIZE));
m_ui->tableSelected->setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(m_ui->bnGetPreview, SIGNAL(clicked()), SLOT(getPreviewImage()));
resourceTypeSelected(0);
}
DlgCreateBundle::~DlgCreateBundle()
{
delete m_ui;
}
QString DlgCreateBundle::bundleName() const
{
return m_ui->editBundleName->text().replace(" ", "_");
}
QString DlgCreateBundle::authorName() const
{
return m_ui->editAuthor->text();
}
QString DlgCreateBundle::email() const
{
return m_ui->editEmail->text();
}
QString DlgCreateBundle::website() const
{
return m_ui->editWebsite->text();
}
QString DlgCreateBundle::license() const
{
return m_ui->editLicense->text();
}
QString DlgCreateBundle::description() const
{
return m_ui->editDescription->document()->toPlainText();
}
QString DlgCreateBundle::saveLocation() const
{
return m_ui->lblSaveLocation->text();
}
QString DlgCreateBundle::previewImage() const
{
return m_previewImage;
}
void DlgCreateBundle::accept()
{
QString name = m_ui->editBundleName->text().remove(" ");
if (name.isEmpty()) {
m_ui->editBundleName->setStyleSheet(QString(" border: 1px solid red"));
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The resource bundle name cannot be empty."));
return;
}
else {
QFileInfo fileInfo(m_ui->lblSaveLocation->text() + "/" + name + ".bundle");
if (fileInfo.exists()) {
m_ui->editBundleName->setStyleSheet("border: 1px solid red");
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("A bundle with this name already exists."));
return;
}
else {
if (!m_bundle) {
KisConfig cfg;
cfg.writeEntry<QString>("BunleExportLocation", m_ui->lblSaveLocation->text());
cfg.writeEntry<QString>("BundleAuthorName", m_ui->editAuthor->text());
cfg.writeEntry<QString>("BundleAuthorEmail", m_ui->editEmail->text());
cfg.writeEntry<QString>("BundleWebsite", m_ui->editWebsite->text());
cfg.writeEntry<QString>("BundleLicense", m_ui->editLicense->text());
}
KDialog::accept();
}
}
}
void DlgCreateBundle::selectSaveLocation()
{
KoFileDialog dlg(this, KoFileDialog::OpenDirectory, "resourcebundlesavelocation");
dlg.setDefaultDir(m_ui->lblSaveLocation->text());
dlg.setCaption(i18n("Select a directory to save the bundle"));
QString location = dlg.url();
m_ui->lblSaveLocation->setText(location);
}
void DlgCreateBundle::addSelected()
{
int row = m_ui->tableAvailable->currentRow();
foreach(QListWidgetItem *item, m_ui->tableAvailable->selectedItems()) {
m_ui->tableSelected->addItem(m_ui->tableAvailable->takeItem(m_ui->tableAvailable->row(item)));
QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString();
if (resourceType == "brushes") {
m_selectedBrushes.append(item->data(Qt::UserRole).toString());
}
else if (resourceType == "presets") {
m_selectedPresets.append(item->data(Qt::UserRole).toString());
}
else if (resourceType == "gradients") {
m_selectedGradients.append(item->data(Qt::UserRole).toString());
}
else if (resourceType == "patterns") {
m_selectedPatterns.append(item->data(Qt::UserRole).toString());
}
else if (resourceType == "palettes") {
m_selectedPalettes.append(item->data(Qt::UserRole).toString());
}
else if (resourceType == "workspaces") {
m_selectedWorkspaces.append(item->data(Qt::UserRole).toString());
}
}
m_ui->tableAvailable->setCurrentRow(row);
}
void DlgCreateBundle::removeSelected()
{
int row = m_ui->tableSelected->currentRow();
foreach(QListWidgetItem *item, m_ui->tableSelected->selectedItems()) {
m_ui->tableAvailable->addItem(m_ui->tableSelected->takeItem(m_ui->tableSelected->row(item)));
QString resourceType = m_ui->cmbResourceTypes->itemData(m_ui->cmbResourceTypes->currentIndex()).toString();
if (resourceType == "brushes") {
m_selectedBrushes.removeAll(item->data(Qt::UserRole).toString());
}
else if (resourceType == "presets") {
m_selectedPresets.removeAll(item->data(Qt::UserRole).toString());
}
else if (resourceType == "gradients") {
m_selectedGradients.removeAll(item->data(Qt::UserRole).toString());
}
else if (resourceType == "patterns") {
m_selectedPatterns.removeAll(item->data(Qt::UserRole).toString());
}
else if (resourceType == "palettes") {
m_selectedPalettes.removeAll(item->data(Qt::UserRole).toString());
}
else if (resourceType == "workspaces") {
m_selectedWorkspaces.removeAll(item->data(Qt::UserRole).toString());
}
}
m_ui->tableSelected->setCurrentRow(row);
}
QPixmap imageToIcon(const QImage &img) {
QPixmap pixmap(ICON_SIZE, ICON_SIZE);
pixmap.fill();
QImage scaled = img.scaled(ICON_SIZE, ICON_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
int x = (ICON_SIZE - scaled.width()) / 2;
int y = (ICON_SIZE - scaled.height()) / 2;
QPainter gc(&pixmap);
gc.drawImage(x, y, scaled);
gc.end();
return pixmap;
}
void DlgCreateBundle::resourceTypeSelected(int idx)
{
QString resourceType = m_ui->cmbResourceTypes->itemData(idx).toString();
m_ui->tableAvailable->clear();
m_ui->tableSelected->clear();
if (resourceType == "brushes") {
KisBrushResourceServer *server = KisBrushServer::instance()->brushServer();
foreach(KisBrushSP res, server->resources()) {
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedBrushes.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
else if (resourceType == "presets") {
KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer();
foreach(KisPaintOpPresetSP res, server->resources()) {
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedPresets.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
else if (resourceType == "gradients") {
KoResourceServer<KoAbstractGradient>* server = KoResourceServerProvider::instance()->gradientServer();
foreach(KoResource *res, server->resources()) {
if (res->filename()!="Foreground to Transparent" && res->filename()!="Foreground to Background") {
//technically we should read from the file-name whether or not the file can be opened, but this works for now. The problem is making sure that bundle-resource know where they are stored.//
- //qDebug()<<res->filename();
+ //dbgKrita<<res->filename();
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedGradients.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
}
else if (resourceType == "patterns") {
KoResourceServer<KoPattern>* server = KoResourceServerProvider::instance()->patternServer();
foreach(KoResource *res, server->resources()) {
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedPatterns.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
else if (resourceType == "palettes") {
KoResourceServer<KoColorSet>* server = KoResourceServerProvider::instance()->paletteServer();
foreach(KoResource *res, server->resources()) {
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedPalettes.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
else if (resourceType == "workspaces") {
KoResourceServer<KisWorkspaceResource>* server = KisResourceServerProvider::instance()->workspaceServer();
foreach(KoResource *res, server->resources()) {
QListWidgetItem *item = new QListWidgetItem(imageToIcon(res->image()), res->name());
item->setData(Qt::UserRole, res->shortFilename());
if (m_selectedWorkspaces.contains(res->shortFilename())) {
m_ui->tableSelected->addItem(item);
}
else {
m_ui->tableAvailable->addItem(item);
}
}
}
}
void DlgCreateBundle::getPreviewImage()
{
KoFileDialog dialog(this, KoFileDialog::OpenFile, "BundlePreviewImage");
dialog.setCaption(i18n("Select file to use as dynamic file layer."));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import));
m_previewImage = dialog.url();
QImage img(m_previewImage);
img = img.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_ui->lblPreview->setPixmap(QPixmap::fromImage(img));
}
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle.cpp b/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
index 556bf40b667..8b0176223b7 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle.cpp
@@ -1,1005 +1,1005 @@
/*
* Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "resourcebundle.h"
#include "resourcebundle_manifest.h"
#include <KoXmlReader.h>
#include <KoXmlWriter.h>
#include <KoStore.h>
#include <KoResourceServerProvider.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <QScopedPointer>
#include <QProcessEnvironment>
#include <QDate>
#include <QDir>
-#include <QDebug>
+#include <kis_debug.h>
#include <QBuffer>
#include <QCryptographicHash>
#include <QByteArray>
#include <QPainter>
#include <QStringList>
#include <QMessageBox>
#include <KoHashGeneratorProvider.h>
#include <KoHashGenerator.h>
#include <kis_resource_server_provider.h>
#include <kis_workspace_resource.h>
#include <kis_paintop_preset.h>
#include <kis_brush_server.h>
#include <kis_debug.h>
#include <calligraversion.h>
#include <calligragitversion.h>
#include "resourcemanager.h"
ResourceBundle::ResourceBundle(QString const& fileName)
: KoResource(fileName),
m_bundleVersion("1")
{
setName(QFileInfo(fileName).baseName());
QString calligraVersion(CALLIGRA_VERSION_STRING);
QString version;
#ifdef CALLIGRA_GIT_SHA1_STRING
QString gitVersion(CALLIGRA_GIT_SHA1_STRING);
version = QString("%1 (git %2)").arg(calligraVersion).arg(gitVersion).toLatin1();
#else
version = calligraVersion;
#endif
m_metadata["generator"] = "Krita (" + version + ")";
}
ResourceBundle::~ResourceBundle()
{
}
QString ResourceBundle::defaultFileExtension() const
{
return QString(".bundle");
}
bool ResourceBundle::load()
{
if (filename().isEmpty()) return false;
QScopedPointer<KoStore> resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
if (!resourceStore || resourceStore->bad()) {
- qWarning() << "Could not open store on bundle" << filename();
+ warnKrita << "Could not open store on bundle" << filename();
m_installed = false;
setValid(false);
return false;
} else {
m_metadata.clear();
bool toRecreate = false;
if (resourceStore->open("META-INF/manifest.xml")) {
if (!m_manifest.load(resourceStore->device())) {
- qWarning() << "Could not open manifest for bundle" << filename();
+ warnKrita << "Could not open manifest for bundle" << filename();
return false;
}
resourceStore->close();
foreach(ResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Bundle is broken. File" << ref.resourcePath << "is missing";
+ warnKrita << "Bundle is broken. File" << ref.resourcePath << "is missing";
toRecreate = true;
}
else {
resourceStore->close();
}
}
if(toRecreate) {
- qWarning() << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated.";
+ warnKrita << "Due to missing files and wrong entries in the manifest, " << filename() << " will be recreated.";
}
} else {
- qWarning() << "Could not load META-INF/manifest.xml";
+ warnKrita << "Could not load META-INF/manifest.xml";
return false;
}
bool versionFound = false;
if (resourceStore->open("meta.xml")) {
KoXmlDocument doc;
if (!doc.setContent(resourceStore->device())) {
- qWarning() << "Could not parse meta.xml for" << filename();
+ warnKrita << "Could not parse meta.xml for" << filename();
return false;
}
// First find the manifest:manifest node.
KoXmlNode n = doc.firstChild();
for (; !n.isNull(); n = n.nextSibling()) {
if (!n.isElement()) {
continue;
}
if (n.toElement().tagName() == "meta:meta") {
break;
}
}
if (n.isNull()) {
- qWarning() << "Could not find manifest node for bundle" << filename();
+ warnKrita << "Could not find manifest node for bundle" << filename();
return false;
}
const KoXmlElement metaElement = n.toElement();
for (n = metaElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
if (n.isElement()) {
KoXmlElement e = n.toElement();
if (e.tagName() == "meta:generator") {
m_metadata.insert("generator", e.firstChild().toText().data());
}
else if (e.tagName() == "dc:author") {
m_metadata.insert("author", e.firstChild().toText().data());
}
else if (e.tagName() == "dc:title") {
m_metadata.insert("title", e.firstChild().toText().data());
}
else if (e.tagName() == "dc:description") {
m_metadata.insert("description", e.firstChild().toText().data());
}
else if (e.tagName() == "meta:initial-creator") {
m_metadata.insert("author", e.firstChild().toText().data());
}
else if (e.tagName() == "dc:creator") {
m_metadata.insert("author", e.firstChild().toText().data());
}
else if (e.tagName() == "meta:creation-date") {
m_metadata.insert("created", e.firstChild().toText().data());
}
else if (e.tagName() == "meta:dc-date") {
m_metadata.insert("updated", e.firstChild().toText().data());
}
else if (e.tagName() == "meta:meta-userdefined") {
if (e.attribute("meta:name") == "tag") {
m_bundletags << e.attribute("meta:value");
}
else {
m_metadata.insert(e.attribute("meta:name"), e.attribute("meta:value"));
}
}
else if(e.tagName() == "meta:bundle-version") {
m_metadata.insert("bundle-version", e.firstChild().toText().data());
versionFound = true;
}
}
}
resourceStore->close();
}
else {
- qWarning() << "Could not load meta.xml";
+ warnKrita << "Could not load meta.xml";
return false;
}
if (resourceStore->open("preview.png")) {
// Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
// fails with "libpng error: IDAT: CRC error"
QByteArray data = resourceStore->device()->readAll();
QBuffer buffer(&data);
m_thumbnail.load(&buffer, "PNG");
resourceStore->close();
}
else {
- qWarning() << "Could not open preview.png";
+ warnKrita << "Could not open preview.png";
}
/*
* If no version is found it's an old bundle with md5 hashes to fix, or if some manifest resource entry
* doesn't not correspond to a file the bundle is "broken", in both cases we need to recreate the bundle.
*/
if(!versionFound) {
m_metadata.insert("bundle-version", "1");
- qWarning() << filename() << " has an old version and possibly wrong resources md5, so it will be recreated.";
+ warnKrita << filename() << " has an old version and possibly wrong resources md5, so it will be recreated.";
toRecreate = true;
}
if(toRecreate) {
recreateBundle(resourceStore);
}
m_installed = true;
setValid(true);
setImage(m_thumbnail);
}
return true;
}
bool ResourceBundle::loadFromDevice(QIODevice *)
{
return false;
}
bool saveResourceToStore(KoResource *resource, KoStore *store, const QString &resType)
{
if (!resource) {
- qWarning() << "No Resource";
+ warnKrita << "No Resource";
return false;
}
if (!resource->valid()) {
- qWarning() << "Resource is not valid";
+ warnKrita << "Resource is not valid";
return false;
}
if (!store || store->bad()) {
- qWarning() << "No Store or Store is Bad";
+ warnKrita << "No Store or Store is Bad";
return false;
}
QByteArray ba;
QBuffer buf;
QFileInfo fi(resource->filename());
if (fi.exists() && fi.isReadable()) {
QFile f(resource->filename());
if (!f.open(QFile::ReadOnly)) {
- qWarning() << "Could not open resource" << resource->filename();
+ warnKrita << "Could not open resource" << resource->filename();
return false;
}
ba = f.readAll();
if (ba.size() == 0) {
- qWarning() << "Resource is empty" << resource->filename();
+ warnKrita << "Resource is empty" << resource->filename();
return false;
}
f.close();
buf.setBuffer(&ba);
}
else {
- qWarning() << "Could not find the resource " << resource->filename() << " or it isn't readable";
+ warnKrita << "Could not find the resource " << resource->filename() << " or it isn't readable";
return false;
}
if (!buf.open(QBuffer::ReadOnly)) {
- qWarning() << "Could not open buffer";
+ warnKrita << "Could not open buffer";
return false;
}
Q_ASSERT(!store->hasFile(resType + "/" + resource->shortFilename()));
if (!store->open(resType + "/" + resource->shortFilename())) {
- qWarning() << "Could not open file in store for resource";
+ warnKrita << "Could not open file in store for resource";
return false;
}
bool res = (store->write(buf.data()) == buf.size());
store->close();
return res;
}
bool ResourceBundle::save()
{
if (filename().isEmpty()) return false;
addMeta("updated", QDate::currentDate().toString("dd/MM/yyyy"));
QDir bundleDir = KGlobal::dirs()->saveLocation("data", "krita/bundles");
bundleDir.cdUp();
QScopedPointer<KoStore> store(KoStore::createStore(filename(), KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
if (!store || store->bad()) return false;
foreach(const QString &resType, m_manifest.types()) {
if (resType == "ko_gradients") {
KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KoResource *res = gradientServer->resourceByMD5(ref.md5sum);
if (!res) res = gradientServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
if (!saveResourceToStore(res, store.data(), "gradients")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
else if (resType == "ko_patterns") {
KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KoResource *res = patternServer->resourceByMD5(ref.md5sum);
if (!res) res = patternServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
if (!saveResourceToStore(res, store.data(), "patterns")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
else if (resType == "kis_brushes") {
KisBrushResourceServer* brushServer = KisBrushServer::instance()->brushServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KisBrushSP brush = brushServer->resourceByMD5(ref.md5sum);
if (!brush) brush = brushServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
KoResource *res = brush.data();
if (!saveResourceToStore(res, store.data(), "brushes")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
else if (resType == "ko_palettes") {
KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KoResource *res = paletteServer->resourceByMD5(ref.md5sum);
if (!res) res = paletteServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
if (!saveResourceToStore(res, store.data(), "palettes")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
else if (resType == "kis_workspaces") {
KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KoResource *res = workspaceServer->resourceByMD5(ref.md5sum);
if (!res) res = workspaceServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
if (!saveResourceToStore(res, store.data(), "workspaces")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
else if (resType == "kis_paintoppresets") {
KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum);
if (!res) res = paintoppresetServer->resourceByFilename(QFileInfo(ref.resourcePath).fileName());
if (!saveResourceToStore(res.data(), store.data(), "paintoppresets")) {
if (res) {
- qWarning() << "Could not save resource" << resType << res->name();
+ warnKrita << "Could not save resource" << resType << res->name();
}
else {
- qWarning() << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
+ warnKrita << "could not find resource for" << QFileInfo(ref.resourcePath).fileName();
}
}
}
}
}
if (!m_thumbnail.isNull()) {
QByteArray byteArray;
QBuffer buffer(&byteArray);
m_thumbnail.save(&buffer, "PNG");
- if (!store->open("preview.png")) qWarning() << "Could not open preview.png";
- if (store->write(byteArray) != buffer.size()) qWarning() << "Could not write preview.png";
+ if (!store->open("preview.png")) warnKrita << "Could not open preview.png";
+ if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png";
store->close();
}
saveManifest(store);
saveMetadata(store);
store->finalize();
return true;
}
bool ResourceBundle::saveToDevice(QIODevice */*dev*/) const
{
return false;
}
bool ResourceBundle::install()
{
QStringList md5Mismatch;
if (filename().isEmpty()) {
- qWarning() << "Cannot install bundle: no file name" << this;
+ warnKrita << "Cannot install bundle: no file name" << this;
return false;
}
QScopedPointer<KoStore> resourceStore(KoStore::createStore(filename(), KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
if (!resourceStore || resourceStore->bad()) {
- qWarning() << "Cannot open the resource bundle: invalid zip file?";
+ warnKrita << "Cannot open the resource bundle: invalid zip file?";
return false;
}
foreach(const QString &resType, m_manifest.types()) {
dbgResources << "Installing resource type" << resType;
if (resType == "gradients") {
KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KoAbstractGradient *res = gradientServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
if (!res->loadFromDevice(resourceStore->device())) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name();
KoAbstractGradient *res2 = gradientServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
gradientServer->addResource(res, false);//add it!
if (!m_gradientsMd5Installed.contains(res->md5())) {
m_gradientsMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
gradientServer->addTag(res, tag);
}
gradientServer->addTag(res, name());
}
else {
- //qWarning() << "Didn't install" << res->name()<<"It already exists on the server";
+ //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
}
}
}
else if (resType == "patterns") {
KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KoPattern *res = patternServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
if (!res->loadFromDevice(resourceStore->device())) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name();
KoPattern *res2 = patternServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
patternServer->addResource(res, false);//add it!
if (!m_patternsMd5Installed.contains(res->md5())) {
m_patternsMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
patternServer->addTag(res, tag);
}
patternServer->addTag(res, name());
}
}
}
else if (resType == "brushes") {
KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KisBrushSP res = brushServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
if (!res->loadFromDevice(resourceStore->device())) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name();
//find the resouce on the server
KisBrushSP res2 = brushServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
brushServer->addResource(res, false);//add it!
if (!m_brushesMd5Installed.contains(res->md5())) {
m_brushesMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
brushServer->addTag(res.data(), tag);
}
brushServer->addTag(res.data(), name());
}
else {
- //qWarning() << "Didn't install" << res->name()<<"It already exists on the server";
+ //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
}
}
}
else if (resType == "palettes") {
KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KoColorSet *res = paletteServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
if (!res->loadFromDevice(resourceStore->device())) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name();
//find the resouce on the server
KoColorSet *res2 = paletteServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
paletteServer->addResource(res, false);//add it!
if (!m_palettesMd5Installed.contains(res->md5())) {
m_palettesMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
paletteServer->addTag(res, tag);
}
paletteServer->addTag(res, name());
}
else {
- //qWarning() << "Didn't install" << res->name()<<"It already exists on the server";
+ //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
}
}
}
else if (resType == "workspaces") {
KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KisWorkspaceResource *res = workspaceServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
if (!res->loadFromDevice(resourceStore->device())) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name();
//the following tries to find the resource by name.
KisWorkspaceResource *res2 = workspaceServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
workspaceServer->addResource(res, false);//add it!
if (!m_workspacesMd5Installed.contains(res->md5())) {
m_workspacesMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
workspaceServer->addTag(res, tag);
}
workspaceServer->addTag(res, name());
}
else {
- //qWarning() << "Didn't install" << res->name()<<"It already exists on the server";
+ //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
}
}
}
else if (resType == "paintoppresets") {
KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files(resType)) {
if (resourceStore->isOpen()) resourceStore->close();
dbgResources << "\tInstalling" << ref.resourcePath;
KisPaintOpPresetSP res = paintoppresetServer->createResource(QString("bundle://%1:%2").arg(filename()).arg(ref.resourcePath));
if (!res) {
- qWarning() << "Could not create resource for" << ref.resourcePath;
+ warnKrita << "Could not create resource for" << ref.resourcePath;
continue;
}
if (!resourceStore->open(ref.resourcePath)) {
- qWarning() << "Failed to open" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to open" << ref.resourcePath << "from bundle" << filename();
continue;
}
// Workaround for some OS (Debian, Ubuntu), where loading directly from the QIODevice
// fails with "libpng error: IDAT: CRC error"
QByteArray data = resourceStore->device()->readAll();
QBuffer buffer(&data);
if (!res->loadFromDevice(&buffer)) {
- qWarning() << "Failed to load" << ref.resourcePath << "from bundle" << filename();
+ warnKrita << "Failed to load" << ref.resourcePath << "from bundle" << filename();
continue;
}
dbgResources << "\t\tresource:" << res->name() << "File:" << res->filename();
//the following tries to find the resource by name.
KisPaintOpPresetSP res2 = paintoppresetServer->resourceByName(res->name());
if (!res2) {//if it doesn't exist...
paintoppresetServer->addResource(res, false);//add it!
if (!m_presetsMd5Installed.contains(res->md5())){
m_presetsMd5Installed.append(res->md5());
}
if (ref.md5sum!=res->md5()) {
md5Mismatch.append(res->name());
}
foreach(const QString &tag, ref.tagList) {
paintoppresetServer->addTag(res.data(), tag);
}
paintoppresetServer->addTag(res.data(), name());
}
else {
- //qWarning() << "Didn't install" << res->name()<<"It already exists on the server";
+ //warnKrita << "Didn't install" << res->name()<<"It already exists on the server";
}
}
}
}
m_installed = true;
if(!md5Mismatch.isEmpty()){
QString message = i18n("The following resources had mismatching MD5 sums. They may have gotten corrupted, for example, during download.");
QMessageBox bundleFeedback;
bundleFeedback.setIcon(QMessageBox::Warning);
foreach (QString name, md5Mismatch) {
message.append("\n");
message.append(name);
}
bundleFeedback.setText(message);
bundleFeedback.exec();
}
return true;
}
bool ResourceBundle::uninstall()
{
m_installed = false;
KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("gradients")) {
foreach(const QByteArray md5, m_gradientsMd5Installed) {
KoAbstractGradient *res = gradientServer->resourceByMD5(md5);
if (res) {
gradientServer->removeResourceFromServer(res);
}
}
KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("patterns")) {
foreach(const QByteArray md5, m_patternsMd5Installed) {
KoPattern *res = patternServer->resourceByMD5(md5);
if (res) {
patternServer->removeResourceFromServer(res);
}
}
KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("brushes")) {
foreach(const QByteArray md5, m_brushesMd5Installed) {
KisBrushSP res = brushServer->resourceByMD5(md5);
if (res) {
brushServer->removeResourceFromServer(res);
}
}
KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("palettes")) {
foreach(const QByteArray md5, m_palettesMd5Installed) {
KoColorSet *res = paletteServer->resourceByMD5(md5);
if (res) {
paletteServer->removeResourceFromServer(res);
}
}
KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("workspaces")) {
foreach(const QByteArray md5, m_workspacesMd5Installed) {
KisWorkspaceResource *res = workspaceServer->resourceByMD5(md5);
if (res) {
workspaceServer->removeResourceFromServer(res);
}
}
KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
//foreach(const ResourceBundleManifest::ResourceReference &ref, m_manifest.files("paintoppresets")) {
foreach(const QByteArray md5, m_presetsMd5Installed) {
KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(md5);
if (res) {
paintoppresetServer->removeResourceFromServer(res);
}
}
return true;
}
void ResourceBundle::addMeta(const QString &type, const QString &value)
{
m_metadata.insert(type, value);
}
const QString ResourceBundle::getMeta(const QString &type, const QString &defaultValue) const
{
if (m_metadata.contains(type)) {
return m_metadata[type];
}
else {
return defaultValue;
}
}
void ResourceBundle::addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum)
{
m_manifest.addResource(fileType, filePath, fileTagList, md5sum);
}
QList<QString> ResourceBundle::getTagsList()
{
return QList<QString>::fromSet(m_bundletags);
}
bool ResourceBundle::isInstalled()
{
return m_installed;
}
QStringList ResourceBundle::resourceTypes()
{
return m_manifest.types();
}
QList<KoResource*> ResourceBundle::resources(const QString &resType)
{
QList<ResourceBundleManifest::ResourceReference> references = m_manifest.files(resType);
QList<KoResource*> ret;
foreach(const ResourceBundleManifest::ResourceReference &ref, references) {
if (resType == "gradients") {
KoResourceServer<KoAbstractGradient>* gradientServer = KoResourceServerProvider::instance()->gradientServer();
KoResource *res = gradientServer->resourceByMD5(ref.md5sum);
if (res) ret << res;
}
else if (resType == "patterns") {
KoResourceServer<KoPattern>* patternServer = KoResourceServerProvider::instance()->patternServer();
KoResource *res = patternServer->resourceByMD5(ref.md5sum);
if (res) ret << res;
}
else if (resType == "brushes") {
KisBrushResourceServer *brushServer = KisBrushServer::instance()->brushServer();
KoResource *res = brushServer->resourceByMD5(ref.md5sum).data();
if (res) ret << res;
}
else if (resType == "palettes") {
KoResourceServer<KoColorSet>* paletteServer = KoResourceServerProvider::instance()->paletteServer();
KoResource *res = paletteServer->resourceByMD5(ref.md5sum);
if (res) ret << res;
}
else if (resType == "workspaces") {
KoResourceServer< KisWorkspaceResource >* workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
KoResource *res = workspaceServer->resourceByMD5(ref.md5sum);
if (res) ret << res;
}
else if (resType == "paintoppresets") {
KisPaintOpPresetResourceServer* paintoppresetServer = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP res = paintoppresetServer->resourceByMD5(ref.md5sum);
if (res) ret << res.data();
}
}
return ret;
}
void ResourceBundle::setThumbnail(QString filename)
{
if (QFileInfo(filename).exists()) {
m_thumbnail = QImage(filename);
m_thumbnail = m_thumbnail.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
Q_ASSERT(!m_thumbnail.isNull());
}
else {
m_thumbnail = QImage(256, 256, QImage::Format_ARGB32);
QPainter gc(&m_thumbnail);
gc.fillRect(0, 0, 256, 256, Qt::red);
gc.end();
}
}
void ResourceBundle::writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer)
{
if (m_metadata.contains(metaKey)) {
writer->startElement(metaTag);
writer->addTextNode(m_metadata[metaKey].toUtf8());
writer->endElement();
}
}
void ResourceBundle::writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer)
{
if (m_metadata.contains(metaKey)) {
writer->startElement("meta:meta-userdefined");
writer->addAttribute("meta:name", metaKey);
writer->addAttribute("meta:value", m_metadata[metaKey]);
writer->endElement();
}
}
void ResourceBundle::setInstalled(bool install)
{
m_installed = install;
}
void ResourceBundle::saveMetadata(QScopedPointer<KoStore> &store)
{
QBuffer buf;
store->open("meta.xml");
buf.open(QBuffer::WriteOnly);
KoXmlWriter metaWriter(&buf);
metaWriter.startDocument("office:document-meta");
metaWriter.startElement("meta:meta");
writeMeta("meta:generator", "generator", &metaWriter);
metaWriter.startElement("meta:bundle-version");
metaWriter.addTextNode(m_bundleVersion.toUtf8());
metaWriter.endElement();
writeMeta("dc:author", "author", &metaWriter);
writeMeta("dc:title", "filename", &metaWriter);
writeMeta("dc:description", "description", &metaWriter);
writeMeta("meta:initial-creator", "author", &metaWriter);
writeMeta("dc:creator", "author", &metaWriter);
writeMeta("meta:creation-date", "created", &metaWriter);
writeMeta("meta:dc-date", "updated", &metaWriter);
writeUserDefinedMeta("email", &metaWriter);
writeUserDefinedMeta("license", &metaWriter);
writeUserDefinedMeta("website", &metaWriter);
foreach (const QString &tag, m_bundletags) {
metaWriter.startElement("meta:meta-userdefined");
metaWriter.addAttribute("meta:name", "tag");
metaWriter.addAttribute("meta:value", tag);
metaWriter.endElement();
}
metaWriter.endElement(); // meta:meta
metaWriter.endDocument();
buf.close();
store->write(buf.data());
store->close();
}
void ResourceBundle::saveManifest(QScopedPointer<KoStore> &store)
{
store->open("META-INF/manifest.xml");
QBuffer buf;
buf.open(QBuffer::WriteOnly);
m_manifest.save(&buf);
buf.close();
store->write(buf.data());
store->close();
}
void ResourceBundle::recreateBundle(QScopedPointer<KoStore> &oldStore)
{
// Save a copy of the unmodified bundle, so that if anything goes bad the user doesn't lose it
QFile file(filename());
file.copy(filename() + ".old");
QString newStoreName = filename() + ".tmp";
QScopedPointer<KoStore> store(KoStore::createStore(newStoreName, KoStore::Write, "application/x-krita-resourcebundle", KoStore::Zip));
KoHashGenerator *generator = KoHashGeneratorProvider::instance()->getGenerator("MD5");
ResourceBundleManifest newManifest;
addMeta("updated", QDate::currentDate().toString("dd/MM/yyyy"));
foreach(ResourceBundleManifest::ResourceReference ref, m_manifest.files()) {
// Wrong manifest entry found, skip it
if(!oldStore->open(ref.resourcePath))
continue;
store->open(ref.resourcePath);
QByteArray data = oldStore->device()->readAll();
oldStore->close();
store->write(data);
store->close();
QByteArray result = generator->generateHash(data);
newManifest.addResource(ref.fileTypeName, ref.resourcePath, ref.tagList, result);
}
m_manifest = newManifest;
if (!m_thumbnail.isNull()) {
QByteArray byteArray;
QBuffer buffer(&byteArray);
m_thumbnail.save(&buffer, "PNG");
- if (!store->open("preview.png")) qWarning() << "Could not open preview.png";
- if (store->write(byteArray) != buffer.size()) qWarning() << "Could not write preview.png";
+ if (!store->open("preview.png")) warnKrita << "Could not open preview.png";
+ if (store->write(byteArray) != buffer.size()) warnKrita << "Could not write preview.png";
store->close();
}
saveManifest(store);
saveMetadata(store);
store->finalize();
// Remove the current bundle and then move the tmp one to be the correct one
file.setFileName(filename());
file.remove();
file.setFileName(newStoreName);
file.rename(filename());
}
diff --git a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
index aeb730047b3..81d69285a0b 100644
--- a/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
+++ b/krita/plugins/extensions/resourcemanager/resourcebundle_manifest.cpp
@@ -1,233 +1,233 @@
/* This file is part of the KDE project
Copyright (C) 2014, Victor Lafon <metabolic.ewilan@hotmail.fr>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "resourcebundle_manifest.h"
#include <QList>
#include <QString>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QDomNodeList>
#include <QForeachContainer>
#include <KoXmlNS.h>
#include <KoXmlReader.h>
#include <KoXmlWriter.h>
#include "KoPattern.h"
#include "KoAbstractGradient.h"
#include "kis_brush_server.h"
#include "kis_resource_server_provider.h"
#include "kis_paintop_preset.h"
#include "kis_workspace_resource.h"
QString resourceTypeToManifestType(const QString &type) {
if (type.startsWith("ko_")) {
return type.mid(3);
}
else if (type.startsWith("kis_")) {
return type.mid(4);
}
else {
return type;
}
}
QString manifestTypeToResourceType(const QString &type) {
if (type == "patterns" || type == "gradients" || type == "palettes") {
return "ko_" + type;
}
else {
return "kis_" + type;
}
}
ResourceBundleManifest::ResourceBundleManifest()
{
}
ResourceBundleManifest::~ResourceBundleManifest()
{
}
bool ResourceBundleManifest::load(QIODevice *device)
{
m_resources.clear();
if (!device->isOpen()) {
if (!device->open(QIODevice::ReadOnly)) {
return false;
}
}
KoXmlDocument manifestDocument;
QString errorMessage;
int errorLine;
int errorColumn;
if (!manifestDocument.setContent(device, true, &errorMessage, &errorLine, &errorColumn)) {
return false;
}
if (!errorMessage.isEmpty()) {
- qWarning() << "Error parsing manifest" << errorMessage << "line" << errorLine << "column" << errorColumn;
+ warnKrita << "Error parsing manifest" << errorMessage << "line" << errorLine << "column" << errorColumn;
return false;
}
// First find the manifest:manifest node.
KoXmlNode n = manifestDocument.firstChild();
for (; !n.isNull(); n = n.nextSibling()) {
if (!n.isElement()) {
continue;
}
if (n.toElement().localName() == "manifest" && n.toElement().namespaceURI() == KoXmlNS::manifest) {
break;
}
}
if (n.isNull()) {
// "Could not find manifest:manifest";
return false;
}
// Now loop through the children of the manifest:manifest and
// store all the manifest:file-entry elements.
const KoXmlElement manifestElement = n.toElement();
for (n = manifestElement.firstChild(); !n.isNull(); n = n.nextSibling()) {
if (!n.isElement())
continue;
KoXmlElement el = n.toElement();
if (!(el.localName() == "file-entry" && el.namespaceURI() == KoXmlNS::manifest))
continue;
QString fullPath = el.attributeNS(KoXmlNS::manifest, "full-path", QString());
QString mediaType = el.attributeNS(KoXmlNS::manifest, "media-type", QString(""));
QString md5sum = el.attributeNS(KoXmlNS::manifest, "md5sum", QString(""));
QString version = el.attributeNS(KoXmlNS::manifest, "version", QString());
QStringList tagList;
KoXmlNode tagNode = n.firstChildElement().firstChildElement();
while (!tagNode.isNull()) {
if (tagNode.firstChild().isText()) {
tagList.append(tagNode.firstChild().toText().data());
}
tagNode = tagNode.nextSibling();
}
// Only if fullPath is valid, should we store this entry.
// If not, we don't bother to find out exactly what is wrong, we just skip it.
if (!fullPath.isNull() && !mediaType.isEmpty() && !md5sum.isEmpty()) {
addResource(mediaType, fullPath, tagList, QByteArray::fromHex(md5sum.toLatin1()));
}
}
return true;
}
bool ResourceBundleManifest::save(QIODevice *device)
{
if (!device->isOpen()) {
if (!device->open(QIODevice::WriteOnly)) {
return false;
}
}
KoXmlWriter manifestWriter(device);
manifestWriter.startDocument("manifest:manifest");
manifestWriter.startElement("manifest:manifest");
manifestWriter.addAttribute("xmlns:manifest", KoXmlNS::manifest);
manifestWriter.addAttribute("manifest:version", "1.2");
manifestWriter.addManifestEntry("/", "application/x-krita-resourcebundle");
foreach(QString resourceType, m_resources.uniqueKeys()) {
foreach(const ResourceReference &resource, m_resources[resourceType].values()) {
manifestWriter.startElement("manifest:file-entry");
manifestWriter.addAttribute("manifest:media-type", resourceTypeToManifestType(resourceType));
manifestWriter.addAttribute("manifest:full-path", resourceTypeToManifestType(resourceType) + "/" + QFileInfo(resource.resourcePath).fileName());
manifestWriter.addAttribute("manifest:md5sum", QString(resource.md5sum.toHex()));
if (!resource.tagList.isEmpty()) {
manifestWriter.startElement("manifest:tags");
foreach(const QString tag, resource.tagList) {
manifestWriter.startElement("manifest:tag");
manifestWriter.addTextNode(tag);
manifestWriter.endElement();
}
manifestWriter.endElement();
}
manifestWriter.endElement();
}
}
manifestWriter.endElement();
manifestWriter.endDocument();
return true;
}
void ResourceBundleManifest::addResource(const QString &fileTypeName, const QString &fileName, const QStringList &fileTagList, const QByteArray &md5)
{
ResourceReference ref(fileName, fileTagList, fileTypeName, md5);
if (!m_resources.contains(fileTypeName)) {
m_resources[fileTypeName] = QMap<QString, ResourceReference>();
}
m_resources[fileTypeName].insert(fileName, ref);
}
QStringList ResourceBundleManifest::types() const
{
return m_resources.keys();
}
QStringList ResourceBundleManifest::tags() const
{
QSet<QString> tags;
foreach(const QString &type, m_resources.keys()) {
foreach(const ResourceReference &ref, m_resources[type].values()) {
tags += ref.tagList.toSet();
}
}
return QStringList::fromSet(tags);
}
QList<ResourceBundleManifest::ResourceReference> ResourceBundleManifest::files(const QString &type) const
{
// If no type is specified we return all the resources
if(type.isEmpty()) {
QList<ResourceReference> resources;
QList<QMap<QString, ResourceReference> >::iterator i;
QList<QMap<QString, ResourceReference> > values = m_resources.values();
for(i = values.begin(); i != values.end(); ++i) {
resources.append(i->values());
}
return resources;
}
else if (!m_resources.contains(type)) {
return QList<ResourceBundleManifest::ResourceReference>();
}
return m_resources[type].values();
}
void ResourceBundleManifest::removeFile(QString fileName)
{
QList<QString> tags;
foreach(const QString &type, m_resources.keys()) {
if (m_resources[type].contains(fileName)) {
m_resources[type].remove(fileName);
}
}
}
diff --git a/krita/plugins/extensions/resourcemanager/resourcemanager.cpp b/krita/plugins/extensions/resourcemanager/resourcemanager.cpp
index 3673d5f1389..6ebcd876cdc 100644
--- a/krita/plugins/extensions/resourcemanager/resourcemanager.cpp
+++ b/krita/plugins/extensions/resourcemanager/resourcemanager.cpp
@@ -1,333 +1,333 @@
/*
* resourcemanager.cc -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "resourcemanager.h"
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QThread>
#include <QMessageBox>
#include <klocale.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kcomponentdata.h>
#include <kpluginfactory.h>
#include <KoFileDialog.h>
#include <KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <kis_debug.h>
#include <kis_action.h>
#include <KisViewManager.h>
#include <kis_resource_server_provider.h>
#include <kis_workspace_resource.h>
#include <kis_paintop_preset.h>
#include <kis_brush_server.h>
#include "dlg_bundle_manager.h"
#include "dlg_create_bundle.h"
ResourceBundleServerProvider::ResourceBundleServerProvider()
{
// user-local
KGlobal::dirs()->addResourceType("kis_resourcebundles", "data", "krita/bundles/");
KGlobal::dirs()->addResourceDir("kis_resourcebundles", QDir::homePath() + QString("/.create/bundles"));
m_resourceBundleServer = new KoResourceServerSimpleConstruction<ResourceBundle>("kis_resourcebundles", "*.bundle");
if (!QFileInfo(m_resourceBundleServer->saveLocation()).exists()) {
QDir().mkpath(m_resourceBundleServer->saveLocation());
}
}
ResourceBundleServerProvider *ResourceBundleServerProvider::instance()
{
K_GLOBAL_STATIC(ResourceBundleServerProvider, s_instance);
return s_instance;
}
ResourceBundleServerProvider::~ResourceBundleServerProvider()
{
delete m_resourceBundleServer;
}
KoResourceServer<ResourceBundle> *ResourceBundleServerProvider::resourceBundleServer()
{
return m_resourceBundleServer;
}
class ResourceManager::Private {
public:
Private()
: loader(0)
{
brushServer = KisBrushServer::instance()->brushServer(false);
paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer(false);
gradientServer = KoResourceServerProvider::instance()->gradientServer(false);
patternServer = KoResourceServerProvider::instance()->patternServer(false);
paletteServer = KoResourceServerProvider::instance()->paletteServer(false);
workspaceServer = KisResourceServerProvider::instance()->workspaceServer(false);
}
KisBrushResourceServer* brushServer;
KisPaintOpPresetResourceServer * paintopServer;
KoResourceServer<KoAbstractGradient>* gradientServer;
KoResourceServer<KoPattern> *patternServer;
KoResourceServer<KoColorSet>* paletteServer;
KoResourceServer<KisWorkspaceResource>* workspaceServer;
QThread *loader;
};
K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin<ResourceManager>();)
ResourceManager::ResourceManager(QObject *parent, const QVariantList &)
: KisViewPlugin(parent)
, d(new Private())
{
QTimer::singleShot(0, this, SLOT(loadBundles()));
KisAction *action = new KisAction(i18n("Import Resources or Bundles..."), this);
addAction("import_resources", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImport()));
action = new KisAction(i18n("Create Resource Bundle..."), this);
addAction("create_bundle", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotCreateBundle()));
action = new KisAction(i18n("Manage Resources..."), this);
addAction("manage_bundles", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotManageBundles()));
}
ResourceManager::~ResourceManager()
{
}
void ResourceManager::slotImport()
{
KoFileDialog dlg(m_view->mainWindow(), KoFileDialog::OpenFiles, "krita_resources");
dlg.setCaption(i18n("Add Resources"));
QMap<QString, QString> filterToTypeMap;
filterToTypeMap[i18n("Krita Brush Presets (*.kpp)")] = "presets";
filterToTypeMap[i18n("GIMP Brushes (*.gbr)")] = "brushes";
filterToTypeMap[i18n("Imagepipe Brushes (*.gih)")] = "brushes";
filterToTypeMap[i18n("Photoshop Brushes (*.abr)")] = "brushes";
filterToTypeMap[i18n("PNG Brushes (*.png)")] = "brushes";
filterToTypeMap[i18n("SVG Brushes (*.svg)")] = "brushes";
filterToTypeMap[i18n("GIMP Gradients (*.ggr)")] = "gradients";
filterToTypeMap[i18n("SVG Gradients (*.svg)")] = "gradients";
filterToTypeMap[i18n("Karbon Gradients (*.kgr)")] = "gradients";
filterToTypeMap[i18n("Resource Bundles (*.bundle)")] = "bundles";
filterToTypeMap[i18n("GIMP Patterns (*.pat)")] = "patterns";
filterToTypeMap[i18n("JPEG Patterns (*.jpg)")] = "patterns";
filterToTypeMap[i18n("GIF Patterns (*.gif)")] = "patterns";
filterToTypeMap[i18n("PNG Patterns (*.png)")] = "patterns";
filterToTypeMap[i18n("TIFF Patterns (*.tif)")] = "patterns";
filterToTypeMap[i18n("XPM Patterns (*.xpm)")] = "patterns";
filterToTypeMap[i18n("BMP Patterns (*.bmp)")] = "patterns";
filterToTypeMap[i18n("Palettes (*.gpl *.pal *.act *.aco *.colors)")] = "palettes";
filterToTypeMap[i18n("Workspaces (*.kts)")] = "workspaces";
QStringList nameFilters = filterToTypeMap.keys();
dlg.setNameFilters(nameFilters, nameFilters[13]); // start with resource bundle as default type (filterToTypeMap is alphabetized)
QStringList resources = dlg.urls();
QString resourceType = dlg.selectedNameFilter();
if (!filterToTypeMap.contains(resourceType)) {
QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("The selected resource type is unknown."));
return;
}
resourceType = filterToTypeMap[resourceType];
if (resourceType == "brushes") {
foreach(const QString &res, resources) {
d->brushServer->importResourceFile(res);
}
}
else if (resourceType == "presets") {
foreach(const QString &res, resources) {
d->paintopServer->importResourceFile(res);
}
}
else if (resourceType == "gradients") {
foreach(const QString &res, resources) {
d->gradientServer->importResourceFile(res);
}
}
else if (resourceType == "bundles") {
foreach(const QString &res, resources) {
ResourceBundle *bundle = ResourceBundleServerProvider::instance()->resourceBundleServer()->createResource(res);
bundle->load();
if (bundle->valid()) {
if (!bundle->install()) {
QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not install the resources for bundle %1.").arg(res));
}
}
else {
QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.").arg(res));
}
QFileInfo fi(res);
QString newFilename = ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension();
QFileInfo fileInfo(newFilename);
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(ResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension());
i++;
}
bundle->setFilename(fileInfo.filePath());
QFile::copy(res, newFilename);
ResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false);
}
}
else if (resourceType == "patterns") {
foreach(const QString &res, resources) {
d->patternServer->importResourceFile(res);
}
}
else if (resourceType == "palettes") {
foreach(const QString &res, resources) {
d->paletteServer->importResourceFile(res);
}
}
else if (resourceType == "workspaces") {
foreach(const QString &res, resources) {
d->workspaceServer->importResourceFile(res);
}
}
else {
- qWarning() << "Trying to add a resource of an undefined type";
+ warnKrita << "Trying to add a resource of an undefined type";
}
}
void ResourceManager::slotCreateBundle()
{
DlgCreateBundle dlgCreateBundle;
if (dlgCreateBundle.exec() != QDialog::Accepted) {
return;
}
QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle";
ResourceBundle* newBundle = new ResourceBundle(bundlePath);
newBundle->addMeta("name", dlgCreateBundle.bundleName());
newBundle->addMeta("author", dlgCreateBundle.authorName());
newBundle->addMeta("email", dlgCreateBundle.email());
newBundle->addMeta("license", dlgCreateBundle.license());
newBundle->addMeta("website", dlgCreateBundle.website());
newBundle->addMeta("description", dlgCreateBundle.description());
QStringList res = dlgCreateBundle.selectedBrushes();
foreach(const QString &r, res) {
KoResource *res = d->brushServer->resourceByFilename(r).data();
newBundle->addResource("kis_brushes", res->filename(), d->brushServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedGradients();
foreach(const QString &r, res) {
KoResource *res = d->gradientServer->resourceByFilename(r);
newBundle->addResource("ko_gradients", res->filename(), d->gradientServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPalettes();
foreach(const QString &r, res) {
KoResource *res = d->paletteServer->resourceByFilename(r);
newBundle->addResource("ko_palettes", res->filename(), d->paletteServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPatterns();
foreach(const QString &r, res) {
KoResource *res = d->patternServer->resourceByFilename(r);
newBundle->addResource("ko_patterns", res->filename(), d->patternServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPresets();
foreach(const QString &r, res) {
KisPaintOpPresetSP preset = d->paintopServer->resourceByFilename(r);
KoResource *res = preset.data();
newBundle->addResource("kis_paintoppresets", res->filename(), d->paintopServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedWorkspaces();
foreach(const QString &r, res) {
KoResource *res = d->workspaceServer->resourceByFilename(r);
newBundle->addResource("kis_workspaces", res->filename(), d->workspaceServer->assignedTagsList(res), res->md5());
}
newBundle->addMeta("fileName", bundlePath);
newBundle->addMeta("created", QDate::currentDate().toString("dd/MM/yyyy"));
newBundle->setThumbnail(dlgCreateBundle.previewImage());
if (!newBundle->save()) {
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle."));
}
}
void ResourceManager::slotManageBundles()
{
DlgBundleManager* dlg = new DlgBundleManager(m_view->actionManager());
if (dlg->exec() != QDialog::Accepted) {
return;
}
}
void ResourceManager::loadBundles()
{
d->loader = new KoResourceLoaderThread(ResourceBundleServerProvider::instance()->resourceBundleServer());
connect(d->loader, SIGNAL(finished()), this, SLOT(bundlesLoaded()));
d->loader->start();
}
void ResourceManager::bundlesLoaded()
{
delete d->loader;
d->loader = 0;
foreach(ResourceBundle *bundle, ResourceBundleServerProvider::instance()->resourceBundleServer()->resources()) {
if (!bundle->install()) {
- qWarning() << "Could not install resources for bundle" << bundle->name();
+ warnKrita << "Could not install resources for bundle" << bundle->name();
}
}
}
#include "resourcemanager.moc"
diff --git a/krita/plugins/filters/colors/kis_color_to_alpha.cpp b/krita/plugins/filters/colors/kis_color_to_alpha.cpp
index 33c957597e5..a8ab8540b44 100644
--- a/krita/plugins/filters/colors/kis_color_to_alpha.cpp
+++ b/krita/plugins/filters/colors/kis_color_to_alpha.cpp
@@ -1,190 +1,190 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_color_to_alpha.h"
#include <QCheckBox>
#include <QSpinBox>
#include <KoColorSpaceMaths.h>
#include <KoUpdater.h>
#include "kis_progress_update_helper.h"
#include <kis_paint_device.h>
#include <kis_selection.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "ui_wdgcolortoalphabase.h"
#include "kis_wdg_color_to_alpha.h"
#include <kis_iterator_ng.h>
KisFilterColorToAlpha::KisFilterColorToAlpha()
: KisFilter(id(), categoryColors(), i18n("&Color to Alpha..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisFilterColorToAlpha::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP) const
{
return new KisWdgColorToAlpha(parent);
}
KisFilterConfiguration* KisFilterColorToAlpha::factoryConfiguration(const KisPaintDeviceSP) const
{
KisFilterConfiguration* config = new KisFilterConfiguration("colortoalpha", 1);
config->setProperty("targetcolor", QColor(255, 255, 255));
config->setProperty("threshold", 100);
return config;
}
template<typename channel_type, typename composite_type>
inline void inverseOver(const int numChannels, const int *channelIndex,
channel_type *dst, const channel_type *baseColor,
qreal dstOpacity)
{
for (int i = 0; i < numChannels; i++) {
const int idx = channelIndex[i];
dst[idx] =
KoColorSpaceMaths<channel_type>::clamp(
(static_cast<composite_type>(dst[idx]) - baseColor[idx]) / dstOpacity + baseColor[idx]);
}
}
template<typename channel_type, typename composite_type>
void applyToIterator(const int numChannels, const int *channelIndex,
KisSequentialIterator &it, KoColor baseColor,
int threshold, const KoColorSpace *cs,
KisProgressUpdateHelper &progressHelper)
{
qreal thresholdF = threshold;
quint8 *baseColorData_uint8 = baseColor.data();
channel_type *baseColorData = reinterpret_cast<channel_type*>(baseColorData_uint8);
do {
channel_type *dst = reinterpret_cast<channel_type*>(it.rawData());
quint8 *dst_uint8 = it.rawData();
quint8 diff = cs->difference(baseColorData_uint8, dst_uint8);
qreal newOpacity = diff >= threshold ? 1.0 : diff / thresholdF;
if(newOpacity < cs->opacityF(dst_uint8)) {
cs->setOpacity(dst_uint8, newOpacity, 1);
}
inverseOver<channel_type, composite_type>(numChannels, channelIndex,
dst, baseColorData,
newOpacity);
progressHelper.step();
} while(it.nextPixel());
}
void KisFilterColorToAlpha::processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfiguration* config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device != 0);
if (config == 0) config = new KisFilterConfiguration("colortoalpha", 1);
QVariant value;
QColor cTA = (config->getProperty("targetcolor", value)) ? value.value<QColor>() : QColor(255, 255, 255);
int threshold = (config->getProperty("threshold", value)) ? value.toInt() : 1;
const KoColorSpace * cs = device->colorSpace();
KisProgressUpdateHelper progressHelper(progressUpdater, 100, rect.width() * rect.height());
KisSequentialIterator it(device, rect);
KoColor baseColor(cTA, cs);
QVector<int> channelIndex;
KoChannelInfo::enumChannelValueType valueType = KoChannelInfo::OTHER;
QList<KoChannelInfo*> channels = cs->channels();
for (int i = 0; i < channels.size(); i++) {
const KoChannelInfo *info = channels[i];
if (info->channelType() != KoChannelInfo::COLOR) continue;
KoChannelInfo::enumChannelValueType currentValueType =
info->channelValueType();
if (valueType != KoChannelInfo::OTHER &&
valueType != currentValueType) {
- qWarning() << "Cannot apply a Color-to-Alpha filter to a heterogeneous colorspace";
+ warnKrita << "Cannot apply a Color-to-Alpha filter to a heterogeneous colorspace";
return;
} else {
valueType = currentValueType;
}
channelIndex.append(i);
}
switch (valueType) {
case KoChannelInfo::UINT8:
applyToIterator<quint8, qint16>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
case KoChannelInfo::UINT16:
applyToIterator<quint16, qint32>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
case KoChannelInfo::UINT32:
applyToIterator<quint32, qint64>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
case KoChannelInfo::FLOAT32:
applyToIterator<float, float>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
case KoChannelInfo::FLOAT64:
applyToIterator<double, double>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
case KoChannelInfo::FLOAT16:
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
applyToIterator<half, half>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
break;
#endif
case KoChannelInfo::INT8: /* !UNSUPPORTED! */
case KoChannelInfo::INT16: /* !UNSUPPORTED! */
case KoChannelInfo::OTHER:
- qWarning() << "Color To Alpha: Unsupported channel type:" << valueType;
+ warnKrita << "Color To Alpha: Unsupported channel type:" << valueType;
}
}
diff --git a/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp b/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
index 35ea3e4ea9a..44f44dfae5f 100644
--- a/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
+++ b/krita/plugins/filters/colorsfilters/kis_perchannel_filter.cpp
@@ -1,582 +1,582 @@
/*
* This file is part of Krita
*
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_perchannel_filter.h"
#include <Qt>
#include <QLayout>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QComboBox>
#include <QDomDocument>
#include <QHBoxLayout>
#include "KoChannelInfo.h"
#include "KoBasicHistogramProducers.h"
#include "KoColorModelStandardIds.h"
#include "KoColorSpace.h"
#include "KoColorTransformation.h"
#include "KoCompositeColorTransformation.h"
#include "KoCompositeOp.h"
#include "KoID.h"
#include "kis_signals_blocker.h"
#include "kis_bookmarked_configuration_manager.h"
#include "kis_config_widget.h"
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include "kis_histogram.h"
#include "kis_painter.h"
#include "widgets/kis_curve_widget.h"
QVector<VirtualChannelInfo> getVirtualChannels(const KoColorSpace *cs)
{
const bool supportsLightness =
cs->colorModelId() != LABAColorModelID &&
cs->colorModelId() != GrayAColorModelID &&
cs->colorModelId() != GrayColorModelID &&
cs->colorModelId() != AlphaColorModelID;
QVector<VirtualChannelInfo> vchannels;
QList<KoChannelInfo *> sortedChannels =
KoChannelInfo::displayOrderSorted(cs->channels());
if (supportsLightness) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::ALL_COLORS, -1, 0, cs);
}
foreach(KoChannelInfo *channel, sortedChannels) {
int pixelIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), sortedChannels);
vchannels << VirtualChannelInfo(VirtualChannelInfo::REAL, pixelIndex, channel, cs);
}
if (supportsLightness) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::LIGHTNESS, -1, 0, cs);
}
return vchannels;
}
KisPerChannelConfigWidget::KisPerChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WFlags f)
: KisConfigWidget(parent, f), m_histogram(0)
{
Q_ASSERT(dev);
m_page = new WdgPerChannel(this);
QHBoxLayout * layout = new QHBoxLayout(this);
Q_CHECK_PTR(layout);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(m_page);
m_dev = dev;
m_activeVChannel = 0;
// fill in the channel chooser, in the display order, but store the pixel index as well.
m_virtualChannels = getVirtualChannels(dev->colorSpace());
const int virtualChannelCount = m_virtualChannels.size();
KisPerChannelFilterConfiguration::initDefaultCurves(m_curves,
virtualChannelCount);
for (int i = 0; i < virtualChannelCount; i++) {
const VirtualChannelInfo &info = m_virtualChannels[i];
m_page->cmbChannel->addItem(info.name(), info.pixelIndex());
m_curves[i].setName(info.name());
}
connect(m_page->cmbChannel, SIGNAL(activated(int)), this, SLOT(setActiveChannel(int)));
// create the horizontal and vertical gradient labels
m_page->hgradient->setPixmap(createGradient(Qt::Horizontal));
m_page->vgradient->setPixmap(createGradient(Qt::Vertical));
// init histogram calculator
QList<QString> keys =
KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_dev->colorSpace());
if(keys.size() > 0) {
KoHistogramProducerFactory *hpf;
hpf = KoHistogramProducerFactoryRegistry::instance()->get(keys.at(0));
m_histogram = new KisHistogram(m_dev, m_dev->exactBounds(), hpf->generate(), LINEAR);
}
connect(m_page->curveWidget, SIGNAL(modified()), this, SIGNAL(sigConfigurationItemChanged()));
m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, 0, 100);
{
KisSignalsBlocker b(m_page->curveWidget);
m_page->curveWidget->setCurve(m_curves[0]);
setActiveChannel(0);
}
}
KisPerChannelConfigWidget::~KisPerChannelConfigWidget()
{
delete m_histogram;
}
inline QPixmap KisPerChannelConfigWidget::createGradient(Qt::Orientation orient /*, int invert (not used yet) */)
{
int width;
int height;
int *i, inc, col;
int x = 0, y = 0;
if (orient == Qt::Horizontal) {
i = &x; inc = 1; col = 0;
width = 256; height = 1;
} else {
i = &y; inc = -1; col = 255;
width = 1; height = 256;
}
QPixmap gradientpix(width, height);
QPainter p(&gradientpix);
p.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine));
for (; *i < 256; (*i)++, col += inc) {
p.setPen(QColor(col, col, col));
p.drawPoint(x, y);
}
return gradientpix;
}
inline QPixmap KisPerChannelConfigWidget::getHistogram()
{
int i;
int height = 256;
QPixmap pix(256, height);
pix.fill();
QPainter p(&pix);
p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
const VirtualChannelInfo &info = m_virtualChannels[m_activeVChannel];
if (m_histogram && info.type() == VirtualChannelInfo::REAL)
{
m_histogram->setChannel(info.pixelIndex());
double highest = (double)m_histogram->calculations().getHighest();
qint32 bins = m_histogram->producer()->numberOfBins();
if (m_histogram->getHistogramType() == LINEAR) {
double factor = (double)height / highest;
for (i = 0; i < bins; ++i) {
p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor));
}
} else {
double factor = (double)height / (double)log(highest);
for (i = 0; i < bins; ++i) {
p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor));
}
}
}
return pix;
}
#define BITS_PER_BYTE 8
#define pwr2(p) (1<<p)
void KisPerChannelConfigWidget::setActiveChannel(int ch)
{
m_curves[m_activeVChannel] = m_page->curveWidget->curve();
m_activeVChannel = ch;
m_page->curveWidget->setCurve(m_curves[m_activeVChannel]);
m_page->curveWidget->setPixmap(getHistogram());
m_page->cmbChannel->setCurrentIndex(m_activeVChannel);
// Getting range accepted by channel
VirtualChannelInfo &currentVChannel = m_virtualChannels[m_activeVChannel];
KoChannelInfo::enumChannelValueType valueType = currentVChannel.valueType();
int order = BITS_PER_BYTE * currentVChannel.channelSize();
int maxValue = pwr2(order);
int min;
int max;
m_page->curveWidget->dropInOutControls();
switch (valueType) {
case KoChannelInfo::UINT8:
case KoChannelInfo::UINT16:
case KoChannelInfo::UINT32:
m_shift = 0;
m_scale = double(maxValue);
min = 0;
max = maxValue - 1;
break;
case KoChannelInfo::INT8:
case KoChannelInfo::INT16:
m_shift = 0.5;
m_scale = double(maxValue);
min = -maxValue / 2;
max = maxValue / 2 - 1;
break;
case KoChannelInfo::FLOAT16:
case KoChannelInfo::FLOAT32:
case KoChannelInfo::FLOAT64:
default:
m_shift = 0;
m_scale = 100.0;
//Hack Alert: should be changed to float
min = 0;
max = 100;
break;
}
m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, min, max);
}
KisPropertiesConfiguration * KisPerChannelConfigWidget::configuration() const
{
int numChannels = m_virtualChannels.size();
KisPerChannelFilterConfiguration * cfg = new KisPerChannelFilterConfiguration(numChannels);
KIS_ASSERT_RECOVER(m_activeVChannel < m_curves.size()) { return cfg; }
m_curves[m_activeVChannel] = m_page->curveWidget->curve();
cfg->setCurves(m_curves);
return cfg;
}
void KisPerChannelConfigWidget::setConfiguration(const KisPropertiesConfiguration * config)
{
const KisPerChannelFilterConfiguration * cfg = dynamic_cast<const KisPerChannelFilterConfiguration *>(config);
if (!cfg)
return;
if (cfg->curves().size() == 0) {
/**
* HACK ALERT: our configuration factory generates
* default configuration with nTransfers==0.
* Catching it here. Just reset all the transfers.
*/
const int virtualChannelCount = m_virtualChannels.size();
KisPerChannelFilterConfiguration::initDefaultCurves(m_curves,
virtualChannelCount);
for (int i = 0; i < virtualChannelCount; i++) {
const VirtualChannelInfo &info = m_virtualChannels[i];
m_curves[i].setName(info.name());
}
} else if (cfg->curves().size() != int(m_virtualChannels.size())) {
- qWarning() << "WARNING: trying to load a curve with incorrect number of channels!";
- qWarning() << "WARNING: expected:" << m_virtualChannels.size();
- qWarning() << "WARNING: got:" << cfg->curves().size();
+ warnKrita << "WARNING: trying to load a curve with incorrect number of channels!";
+ warnKrita << "WARNING: expected:" << m_virtualChannels.size();
+ warnKrita << "WARNING: got:" << cfg->curves().size();
return;
} else {
for (int ch = 0; ch < cfg->curves().size(); ch++)
m_curves[ch] = cfg->curves()[ch];
}
// HACK: we save the previous curve in setActiveChannel, so just copy it
m_page->curveWidget->setCurve(m_curves[m_activeVChannel]);
setActiveChannel(0);
}
KisPerChannelFilterConfiguration::KisPerChannelFilterConfiguration(int nCh)
: KisColorTransformationConfiguration("perchannel", 1)
{
initDefaultCurves(m_curves, nCh);
updateTransfers();
}
KisPerChannelFilterConfiguration::~KisPerChannelFilterConfiguration()
{
}
bool KisPerChannelFilterConfiguration::isCompatible(const KisPaintDeviceSP dev) const
{
return (int)dev->colorSpace()->channelCount() == m_curves.size();
}
void KisPerChannelFilterConfiguration::setCurves(QList<KisCubicCurve> &curves)
{
m_curves.clear();
m_curves = curves;
updateTransfers();
}
void KisPerChannelFilterConfiguration::initDefaultCurves(QList<KisCubicCurve> &curves, int nCh)
{
curves.clear();
for (int i = 0; i < nCh; i++) {
curves.append(KisCubicCurve());
}
}
void KisPerChannelFilterConfiguration::updateTransfers()
{
m_transfers.resize(m_curves.size());
for (int i = 0; i < m_curves.size(); i++) {
m_transfers[i] = m_curves[i].uint16Transfer();
}
}
const QVector<QVector<quint16> >&
KisPerChannelFilterConfiguration::transfers() const
{
return m_transfers;
}
const QList<KisCubicCurve>&
KisPerChannelFilterConfiguration::curves() const
{
return m_curves;
}
void KisPerChannelFilterConfiguration::fromLegacyXML(const QDomElement& root)
{
fromXML(root);
}
void KisPerChannelFilterConfiguration::fromXML(const QDomElement& root)
{
QList<KisCubicCurve> curves;
quint16 numTransfers = 0;
int version;
version = root.attribute("version").toInt();
QDomElement e = root.firstChild().toElement();
QString attributeName;
KisCubicCurve curve;
quint16 index;
while (!e.isNull()) {
if ((attributeName = e.attribute("name")) == "nTransfers") {
numTransfers = e.text().toUShort();
} else {
QRegExp rx("curve(\\d+)");
if (rx.indexIn(attributeName, 0) != -1) {
index = rx.cap(1).toUShort();
index = qMin(index, quint16(curves.count()));
if (!e.text().isEmpty()) {
curve.fromString(e.text());
}
curves.insert(index, curve);
}
}
e = e.nextSiblingElement();
}
if (!numTransfers)
return;
setVersion(version);
setCurves(curves);
}
/**
* Inherited from KisPropertiesConfiguration
*/
//void KisPerChannelFilterConfiguration::fromXML(const QString& s)
void addParamNode(QDomDocument& doc,
QDomElement& root,
const QString &name,
const QString &value)
{
QDomText text = doc.createTextNode(value);
QDomElement t = doc.createElement("param");
t.setAttribute("name", name);
t.appendChild(text);
root.appendChild(t);
}
void KisPerChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const
{
/**
* <params version=1>
* <param name="nTransfers">3</param>
* <param name="curve0">0,0;0.5,0.5;1,1;</param>
* <param name="curve1">0,0;1,1;</param>
* <param name="curve2">0,0;1,1;</param>
* </params>
*/
root.setAttribute("version", version());
QDomText text;
QDomElement t;
addParamNode(doc, root, "nTransfers", QString::number(m_curves.size()));
KisCubicCurve curve;
QString paramName;
for (int i = 0; i < m_curves.size(); ++i) {
QString name = QLatin1String("curve") + QString::number(i);
QString value = m_curves[i].toString();
addParamNode(doc, root, name, value);
}
}
/**
* Inherited from KisPropertiesConfiguration
*/
//QString KisPerChannelFilterConfiguration::toXML()
KisPerChannelFilter::KisPerChannelFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Color Adjustment curves..."))
{
setSupportsPainting(true);
setColorSpaceIndependence(TO_LAB16);
}
KisConfigWidget * KisPerChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const
{
return new KisPerChannelConfigWidget(parent, dev);
}
KisFilterConfiguration * KisPerChannelFilter::factoryConfiguration(const KisPaintDeviceSP) const
{
return new KisPerChannelFilterConfiguration(0);
}
KoColorTransformation* KisPerChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
const KisPerChannelFilterConfiguration* configBC =
dynamic_cast<const KisPerChannelFilterConfiguration*>(config); // Somehow, this shouldn't happen
Q_ASSERT(configBC);
const QVector<QVector<quint16> > &originalTransfers = configBC->transfers();
const QList<KisCubicCurve> &originalCurves = configBC->curves();
/**
* TODO: What about the order of channels? (DK)
*
* Virtual channels are sorted in display order, does Lcms accepts
* transforms in display order? Why on Earth it works?! Is it
* documented anywhere?
*/
const QVector<VirtualChannelInfo> virtualChannels = getVirtualChannels(cs);
if (originalTransfers.size() != int(virtualChannels.size())) {
// We got an illegal number of colorchannels :(
return 0;
}
bool colorsNull = true;
bool lightnessNull = true;
bool allColorsNull = true;
int alphaIndexInReal = -1;
QVector<QVector<quint16> > realTransfers;
QVector<quint16> lightnessTransfer;
QVector<quint16> allColorsTransfer;
for (int i = 0; i < virtualChannels.size(); i++) {
if (virtualChannels[i].type() == VirtualChannelInfo::REAL) {
realTransfers << originalTransfers[i];
if (virtualChannels[i].isAlpha()) {
alphaIndexInReal = realTransfers.size() - 1;
}
if (colorsNull && !originalCurves[i].isNull()) {
colorsNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::LIGHTNESS) {
KIS_ASSERT_RECOVER_NOOP(lightnessTransfer.isEmpty());
lightnessTransfer = originalTransfers[i];
if (lightnessNull && !originalCurves[i].isNull()) {
lightnessNull = false;
}
} else if (virtualChannels[i].type() == VirtualChannelInfo::ALL_COLORS) {
KIS_ASSERT_RECOVER_NOOP(allColorsTransfer.isEmpty());
allColorsTransfer = originalTransfers[i];
if (allColorsNull && !originalCurves[i].isNull()) {
allColorsNull = false;
}
}
}
KoColorTransformation *lightnessTransform = 0;
KoColorTransformation *allColorsTransform = 0;
KoColorTransformation *colorTransform = 0;
if (!colorsNull) {
const quint16** transfers = new const quint16*[realTransfers.size()];
for(int i = 0; i < realTransfers.size(); ++i) {
transfers[i] = realTransfers[i].constData();
/**
* createPerChannelAdjustment() expects alpha channel to
* be the last channel in the list, so just it here
*/
KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
alphaIndexInReal == (realTransfers.size() - 1));
}
colorTransform = cs->createPerChannelAdjustment(transfers);
delete [] transfers;
}
if (!lightnessNull) {
lightnessTransform = cs->createBrightnessContrastAdjustment(lightnessTransfer.constData());
}
if (!allColorsNull) {
const quint16** allColorsTransfers = new const quint16*[realTransfers.size()];
for(int i = 0; i < realTransfers.size(); ++i) {
allColorsTransfers[i] = (i != alphaIndexInReal) ?
allColorsTransfer.constData() : 0;
/**
* createPerChannelAdjustment() expects alpha channel to
* be the last channel in the list, so just it here
*/
KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
alphaIndexInReal == (realTransfers.size() - 1));
}
allColorsTransform = cs->createPerChannelAdjustment(allColorsTransfers);
delete[] allColorsTransfers;
}
QVector<KoColorTransformation*> allTransforms;
allTransforms << lightnessTransform;
allTransforms << allColorsTransform;
allTransforms << colorTransform;
return KoCompositeColorTransformation::createOptimizedCompositeTransform(allTransforms);
}
diff --git a/krita/plugins/filters/levelfilter/kis_level_filter.cpp b/krita/plugins/filters/levelfilter/kis_level_filter.cpp
index dc7d21799ae..3826bae0d61 100644
--- a/krita/plugins/filters/levelfilter/kis_level_filter.cpp
+++ b/krita/plugins/filters/levelfilter/kis_level_filter.cpp
@@ -1,309 +1,309 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Frederic Coiffier <fcoiffie@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_level_filter.h"
#include <cmath>
#include <klocale.h>
#include <QtGlobal>
#include <QLayout>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QSpinBox>
#include <KoBasicHistogramProducers.h>
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include "kis_paint_device.h"
#include "kis_histogram.h"
#include "kis_painter.h"
#include "kis_gradient_slider.h"
#include "kis_processing_information.h"
#include "kis_selection.h"
#include "kis_types.h"
#include "filter/kis_color_transformation_configuration.h"
KisLevelFilter::KisLevelFilter()
: KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Levels..."))
{
setShortcut(KShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)));
setSupportsPainting(false);
setColorSpaceIndependence(TO_LAB16);
}
KisLevelFilter::~KisLevelFilter()
{
}
KisConfigWidget * KisLevelFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const
{
return new KisLevelConfigWidget(parent, dev);
}
KoColorTransformation* KisLevelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfiguration* config) const
{
if (!config) {
warnKrita << "No configuration object for level filter\n";
return 0;
}
Q_ASSERT(config);
int blackvalue = config->getInt("blackvalue");
int whitevalue = config->getInt("whitevalue", 255);
double gammavalue = config->getDouble("gammavalue", 1.0);
int outblackvalue = config->getInt("outblackvalue");
int outwhitevalue = config->getInt("outwhitevalue", 255);
quint16 transfer[256];
for (int i = 0; i < 256; i++) {
if (i <= blackvalue)
transfer[i] = outblackvalue;
else if (i < whitevalue) {
double a = (double)(i - blackvalue) / (double)(whitevalue - blackvalue);
a = (double)(outwhitevalue - outblackvalue) * pow(a, (1.0 / gammavalue));
transfer[i] = int(outblackvalue + a);
} else
transfer[i] = outwhitevalue;
// TODO use floats instead of integer in the configuration
transfer[i] = ((int)transfer[i] * 0xFFFF) / 0xFF ;
}
return cs->createBrightnessContrastAdjustment(transfer);
}
KisLevelConfigWidget::KisLevelConfigWidget(QWidget * parent, KisPaintDeviceSP dev)
: KisConfigWidget(parent)
{
m_page.setupUi(this);
m_page.ingradient->enableGamma(true);
m_page.blackspin->setValue(0);
m_page.whitespin->setValue(255);
m_page.gammaspin->setValue(1.0);
m_page.ingradient->slotModifyGamma(1.0);
m_page.outblackspin->setValue(0);
m_page.outwhitespin->setValue(255);
connect(m_page.blackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.blackspin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyBlack(int)));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyWhite(int)));
connect(m_page.gammaspin, SIGNAL(valueChanged(double)), m_page.ingradient, SLOT(slotModifyGamma(double)));
connect(m_page.blackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInWhiteLimit(int)));
connect(m_page.whitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInBlackLimit(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedBlack(int)), m_page.blackspin, SLOT(setValue(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedWhite(int)), m_page.whitespin, SLOT(setValue(int)));
connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), m_page.gammaspin, SLOT(setValue(double)));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyBlack(int)));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyWhite(int)));
connect(m_page.outblackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutWhiteLimit(int)));
connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutBlackLimit(int)));
connect(m_page.outgradient, SIGNAL(sigModifiedBlack(int)), m_page.outblackspin, SLOT(setValue(int)));
connect(m_page.outgradient, SIGNAL(sigModifiedWhite(int)), m_page.outwhitespin, SLOT(setValue(int)));
connect(m_page.butauto, SIGNAL(clicked(bool)), this, SLOT(slotAutoLevel(void)));
connect((QObject*)(m_page.chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(slotDrawHistogram(bool)));
KoHistogramProducerSP producer = KoHistogramProducerSP(new KoGenericLabHistogramProducer());
m_histogram.reset( new KisHistogram(dev, dev->exactBounds(), producer, LINEAR) );
m_histlog = false;
m_page.histview->resize(288,100);
slotDrawHistogram();
}
KisLevelConfigWidget::~KisLevelConfigWidget()
{
}
void KisLevelConfigWidget::slotDrawHistogram(bool logarithmic)
{
int wHeight = m_page.histview->height();
int wHeightMinusOne = wHeight - 1;
int wWidth = m_page.histview->width();
if (m_histlog != logarithmic) {
// Update the m_histogram
if (logarithmic)
m_histogram->setHistogramType(LOGARITHMIC);
else
m_histogram->setHistogramType(LINEAR);
m_histlog = logarithmic;
}
QPixmap pix(wWidth-100, wHeight);
pix.fill();
QPainter p(&pix);
p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
double highest = (double)m_histogram->calculations().getHighest();
qint32 bins = m_histogram->producer()->numberOfBins();
// use nearest neighbour interpolation
if (m_histogram->getHistogramType() == LINEAR) {
double factor = (double)(wHeight - wHeight / 5.0) / highest;
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1));
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - (int)m_histogram->getValue(binNo) * factor);
}
} else {
double factor = (double)(wHeight - wHeight / 5.0) / (double)log(highest);
for (int i = 0; i < wWidth; i++) {
int binNo = qRound((double)i / wWidth * (bins - 1)) ;
if ((int)m_histogram->getValue(binNo) != 0)
p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - log((double)m_histogram->getValue(binNo)) * factor);
}
}
m_page.histview->setPixmap(pix);
}
void KisLevelConfigWidget::slotModifyInBlackLimit(int limit)
{
m_page.blackspin->setMaximum(limit - 1);
}
void KisLevelConfigWidget::slotModifyInWhiteLimit(int limit)
{
m_page.whitespin->setMinimum(limit + 1);
}
void KisLevelConfigWidget::slotModifyOutBlackLimit(int limit)
{
m_page.outblackspin->setMaximum(limit - 1);
}
void KisLevelConfigWidget::slotModifyOutWhiteLimit(int limit)
{
m_page.outwhitespin->setMinimum(limit + 1);
}
void KisLevelConfigWidget::slotAutoLevel(void)
{
Q_ASSERT(m_histogram);
qint32 num_bins = m_histogram->producer()->numberOfBins();
Q_ASSERT(num_bins > 1);
int chosen_low_bin = 0, chosen_high_bin = num_bins-1;
int count_thus_far = m_histogram->getValue(0);
const int total_count = m_histogram->producer()->count();
const double threshold = 0.006;
// find the low and hi point/bins based on summing count percentages
//
// this implementation is a port of GIMP's auto level implementation
// (use a GPLv2 version as reference, specifically commit 51bfd07f18ef045a3e43632218fd92cae9ff1e48)
for (int bin=0; bin<(num_bins-1); ++bin) {
int next_count_thus_far = count_thus_far + m_histogram->getValue(bin+1);
double this_percentage = static_cast<double>(count_thus_far) / total_count;
double next_percentage = static_cast<double>(next_count_thus_far) / total_count;
- //qDebug() << "bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
+ //dbgKrita << "bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) {
chosen_low_bin = bin;
break;
}
count_thus_far = next_count_thus_far;
}
count_thus_far = m_histogram->getValue(num_bins-1);
for (int bin=(num_bins-1); bin>0; --bin) {
int next_count_thus_far = count_thus_far + m_histogram->getValue(bin-1);
double this_percentage = static_cast<double>(count_thus_far) / total_count;
double next_percentage = static_cast<double>(next_count_thus_far) / total_count;
- //qDebug() << "hi-bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
+ //dbgKrita << "hi-bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage;
if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) {
chosen_high_bin = bin;
break;
}
count_thus_far = next_count_thus_far;
}
if (chosen_low_bin < chosen_high_bin) {
m_page.blackspin->setValue(chosen_low_bin);
m_page.ingradient->slotModifyBlack(chosen_low_bin);
m_page.whitespin->setValue(chosen_high_bin);
m_page.ingradient->slotModifyWhite(chosen_high_bin);
}
}
KisPropertiesConfiguration * KisLevelConfigWidget::configuration() const
{
KisColorTransformationConfiguration * config = new KisColorTransformationConfiguration(KisLevelFilter::id().id(), 1);
config->setProperty("blackvalue", m_page.blackspin->value());
config->setProperty("whitevalue", m_page.whitespin->value());
config->setProperty("gammavalue", m_page.ingradient->getGamma());
config->setProperty("outblackvalue", m_page.outblackspin->value());
config->setProperty("outwhitevalue", m_page.outwhitespin->value());
return config;
}
void KisLevelConfigWidget::setConfiguration(const KisPropertiesConfiguration * config)
{
QVariant value;
if (config->getProperty("blackvalue", value)) {
m_page.blackspin->setValue(value.toUInt());
m_page.ingradient->slotModifyBlack(value.toUInt());
}
if (config->getProperty("whitevalue", value)) {
m_page.whitespin->setValue(value.toUInt());
m_page.ingradient->slotModifyWhite(value.toUInt());
}
if (config->getProperty("gammavalue", value)) {
m_page.gammaspin->setValue(value.toUInt());
m_page.ingradient->slotModifyGamma(value.toDouble());
}
if (config->getProperty("outblackvalue", value)) {
m_page.outblackspin->setValue(value.toUInt());
m_page.outgradient->slotModifyBlack(value.toUInt());
}
if (config->getProperty("outwhitevalue", value)) {
m_page.outwhitespin->setValue(value.toUInt());
m_page.outgradient->slotModifyWhite(value.toUInt());
}
}
diff --git a/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp b/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
index de493e14a14..f989076b18e 100644
--- a/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
+++ b/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_config_widget.cpp
@@ -1,186 +1,186 @@
/*
* Copyright (c) 2010-2011 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_phong_bumpmap_config_widget.h"
#include <filter/kis_filter_configuration.h>
#include <kis_size_group.h>
#include "phong_bumpmap_constants.h"
#include "KoChannelInfo.h"
#include "KoColorSpace.h"
KisPhongBumpmapConfigWidget::KisPhongBumpmapConfigWidget(const KisPaintDeviceSP dev, QWidget *parent, Qt::WFlags f)
: KisConfigWidget(parent, f)
, m_device(dev)
{
Q_ASSERT(m_device);
m_page = new KisPhongBumpmapWidget(this);
KisSizeGroup *matPropLabelsGroup = new KisSizeGroup(this);
matPropLabelsGroup->addWidget(m_page->lblAmbientReflectivity);
matPropLabelsGroup->addWidget(m_page->lblDiffuseReflectivity);
matPropLabelsGroup->addWidget(m_page->lblSpecularReflectivity);
matPropLabelsGroup->addWidget(m_page->lblSpecularShinyExp);
// Connect widgets to each other
connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox1, SLOT(setValue(int)));
connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox2, SLOT(setValue(int)));
connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox3, SLOT(setValue(int)));
connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), m_page->azimuthSpinBox4, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox1, SIGNAL(valueChanged(int)), m_page->azimuthDial1, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox2, SIGNAL(valueChanged(int)), m_page->azimuthDial2, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox3, SIGNAL(valueChanged(int)), m_page->azimuthDial3, SLOT(setValue(int)));
connect(m_page->azimuthSpinBox4, SIGNAL(valueChanged(int)), m_page->azimuthDial4, SLOT(setValue(int)));
//Let widgets warn the preview of when they are updated
connect(m_page->azimuthDial1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->azimuthDial4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo1, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo2, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo3, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightKColorCombo4, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox1, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox2, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox3, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->inclinationSpinBox4, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->useNormalMap, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->diffuseReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->specularReflectivityGroup, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->ambientReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->diffuseReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->specularReflectivityKisDoubleSliderSpinBox, SIGNAL(valueChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->shinynessExponentKisSliderSpinBox, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->heightChannelComboBox, SIGNAL(currentIndexChanged(int)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox1, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox2, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox3, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
connect(m_page->lightSourceGroupBox4, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged()));
QVBoxLayout *l = new QVBoxLayout(this);
Q_CHECK_PTR(l);
l->addWidget(m_page);
/* fill in the channel chooser */
QList<KoChannelInfo *> channels = m_device->colorSpace()->channels();
for (quint8 ch = 0; ch < m_device->colorSpace()->colorChannelCount(); ch++)
m_page->heightChannelComboBox->addItem(channels.at(ch)->name());
connect(m_page->useNormalMap, SIGNAL(toggled(bool)), this, SLOT(slotDisableHeightChannelCombobox(bool) ) );
}
void KisPhongBumpmapConfigWidget::setConfiguration(const KisPropertiesConfiguration *config)
{
if (!config) return;
QVariant tempcolor;
if (config->getBool(USE_NORMALMAP_IS_ENABLED)) {
m_page->heightChannelComboBox->setEnabled(false);
} else {
m_page->heightChannelComboBox->setEnabled(true);
}
m_page->ambientReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_AMBIENT_REFLECTIVITY) );
m_page->diffuseReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_DIFFUSE_REFLECTIVITY) );
m_page->specularReflectivityKisDoubleSliderSpinBox->setValue( config->getDouble(PHONG_SPECULAR_REFLECTIVITY) );
m_page->shinynessExponentKisSliderSpinBox->setValue( config->getInt(PHONG_SHINYNESS_EXPONENT) );
m_page->useNormalMap->setChecked( config->getBool(USE_NORMALMAP_IS_ENABLED) );
m_page->diffuseReflectivityGroup->setChecked( config->getBool(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED) );
m_page->specularReflectivityGroup->setChecked( config->getBool(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED) );
// NOTE: Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
m_page->lightSourceGroupBox1->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[0]) );
m_page->lightSourceGroupBox2->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[1]) );
m_page->lightSourceGroupBox3->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[2]) );
m_page->lightSourceGroupBox4->setChecked( config->getBool(PHONG_ILLUMINANT_IS_ENABLED[3]) );
config->getProperty(PHONG_ILLUMINANT_COLOR[0], tempcolor);
m_page->lightKColorCombo1->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[1], tempcolor);
m_page->lightKColorCombo2->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[2], tempcolor);
m_page->lightKColorCombo3->setColor(tempcolor.value<QColor>());
config->getProperty(PHONG_ILLUMINANT_COLOR[3], tempcolor);
m_page->lightKColorCombo4->setColor(tempcolor.value<QColor>());
m_page->azimuthSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[0]) );
m_page->azimuthSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[1]) );
m_page->azimuthSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[2]) );
m_page->azimuthSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_AZIMUTH[3]) );
m_page->inclinationSpinBox1->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[0]) );
m_page->inclinationSpinBox2->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[1]) );
m_page->inclinationSpinBox3->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[2]) );
m_page->inclinationSpinBox4->setValue( config->getDouble(PHONG_ILLUMINANT_INCLINATION[3]) );
}
KisPropertiesConfiguration *KisPhongBumpmapConfigWidget::configuration() const
{
KisFilterConfiguration *config = new KisFilterConfiguration("phongbumpmap", 2);
config->setProperty(PHONG_HEIGHT_CHANNEL, m_page->heightChannelComboBox->currentText());
config->setProperty(USE_NORMALMAP_IS_ENABLED, m_page->useNormalMap->isChecked());
config->setProperty(PHONG_AMBIENT_REFLECTIVITY, m_page->ambientReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY, m_page->diffuseReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_SPECULAR_REFLECTIVITY, m_page->specularReflectivityKisDoubleSliderSpinBox->value());
config->setProperty(PHONG_SHINYNESS_EXPONENT, m_page->shinynessExponentKisSliderSpinBox->value());
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED, m_page->diffuseReflectivityGroup->isChecked());
config->setProperty(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED, m_page->specularReflectivityGroup->isChecked());
//config->setProperty(PHONG_SHINYNESS_EXPONENT_IS_ENABLED, m_page->specularReflectivityCheckBox->isChecked());
// Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[0], m_page->lightSourceGroupBox1->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[1], m_page->lightSourceGroupBox2->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[2], m_page->lightSourceGroupBox3->isChecked());
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[3], m_page->lightSourceGroupBox4->isChecked());
config->setProperty(PHONG_ILLUMINANT_COLOR[0], m_page->lightKColorCombo1->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[1], m_page->lightKColorCombo2->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[2], m_page->lightKColorCombo3->color());
config->setProperty(PHONG_ILLUMINANT_COLOR[3], m_page->lightKColorCombo4->color());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[0], m_page->azimuthSpinBox1->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[1], m_page->azimuthSpinBox2->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[2], m_page->azimuthSpinBox3->value());
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[3], m_page->azimuthSpinBox4->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[0], m_page->inclinationSpinBox1->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[1], m_page->inclinationSpinBox2->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[2], m_page->inclinationSpinBox3->value());
config->setProperty(PHONG_ILLUMINANT_INCLINATION[3], m_page->inclinationSpinBox4->value());
// Read configuration
/*
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(config->getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
- qDebug() << i.key() << ":" << i.value();
+ dbgKrita << i.key() << ":" << i.value();
*/
return config;
}
void KisPhongBumpmapConfigWidget::slotDisableHeightChannelCombobox(bool normalmapchecked) {
if (normalmapchecked) {
m_page->heightChannelComboBox->setEnabled(false);
} else {
m_page->heightChannelComboBox->setEnabled(true);
}
}
diff --git a/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp b/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
index 41cc7712f76..df94b16e8d9 100644
--- a/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
+++ b/krita/plugins/filters/phongbumpmap/kis_phong_bumpmap_filter.cpp
@@ -1,237 +1,237 @@
/*
* Copyright (c) 2010-2011 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_phong_bumpmap_filter.h"
#include "kis_phong_bumpmap_config_widget.h"
#include "phong_pixel_processor.h"
#include "kis_debug.h"
#include "kis_paint_device.h"
#include "kis_config_widget.h"
#include "KoUpdater.h"
#include "kis_math_toolbox.h"
#include "KoColorSpaceRegistry.h"
#include <KoChannelInfo.h>
#include <filter/kis_filter_configuration.h>
#include "kis_iterator_ng.h"
#include "kundo2command.h"
#include "kis_painter.h"
KisFilterPhongBumpmap::KisFilterPhongBumpmap()
: KisFilter(KoID("phongbumpmap" , i18n("PhongBumpmap")),
KisFilter::categoryMap(), i18n("&PhongBumpmap..."))
{
setColorSpaceIndependence(TO_LAB16);
setSupportsPainting(true);
}
void KisFilterPhongBumpmap::processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfiguration *config,
KoUpdater *progressUpdater
) const
{
if (!config) return;
if (progressUpdater) progressUpdater->setProgress(0);
QString userChosenHeightChannel = config->getString(PHONG_HEIGHT_CHANNEL, "FAIL");
bool m_usenormalmap = config->getBool(USE_NORMALMAP_IS_ENABLED);
if (userChosenHeightChannel == "FAIL") {
qDebug("FIX YOUR FILTER");
return;
}
KoChannelInfo *m_heightChannel = 0;
foreach (KoChannelInfo* channel, device->colorSpace()->channels()) {
if (userChosenHeightChannel == channel->name()) {
m_heightChannel = channel;
}
}
if (!m_heightChannel) {
m_heightChannel = device->colorSpace()->channels().first();
}
KIS_ASSERT_RECOVER_RETURN(m_heightChannel);
QRect inputArea = applyRect;
QRect outputArea = applyRect;
if (m_usenormalmap==false) {
inputArea.adjust(-1, -1, 1, 1);
}
quint32 posup;
quint32 posdown;
quint32 posleft;
quint32 posright;
QColor I; //Reflected light
if (progressUpdater) progressUpdater->setProgress(1);
//======Preparation paraphlenalia=======
//Hardcoded facts about Phong Bumpmap: it _will_ generate an RGBA16 bumpmap
const quint8 BYTE_DEPTH_OF_BUMPMAP = 2; // 16 bits per channel
const quint8 CHANNEL_COUNT_OF_BUMPMAP = 4; // RGBA
const quint32 pixelsOfInputArea = abs(inputArea.width() * inputArea.height());
const quint32 pixelsOfOutputArea = abs(outputArea.width() * outputArea.height());
const quint8 pixelSize = BYTE_DEPTH_OF_BUMPMAP * CHANNEL_COUNT_OF_BUMPMAP;
const quint32 bytesToFillBumpmapArea = pixelsOfOutputArea * pixelSize;
QVector<quint8> bumpmap(bytesToFillBumpmapArea);
quint8 *bumpmapDataPointer = bumpmap.data();
quint32 ki = KoChannelInfo::displayPositionToChannelIndex(m_heightChannel->displayPosition(), device->colorSpace()->channels());
PhongPixelProcessor tileRenderer(pixelsOfInputArea, config);
if (progressUpdater) progressUpdater->setProgress(2);
//===============RENDER=================
QVector<PtrToDouble> toDoubleFuncPtr(device->colorSpace()->channels().count());
KisMathToolbox *mathToolbox = KisMathToolboxRegistry::instance()->value(device->colorSpace()->mathToolboxId().id());
if (!mathToolbox->getToDoubleChannelPtr(device->colorSpace()->channels(), toDoubleFuncPtr)) {
return;
}
KisHLineConstIteratorSP iterator;
quint32 curPixel = 0;
iterator = device->createHLineConstIteratorNG(inputArea.x(),
inputArea.y(),
inputArea.width()
);
if (m_usenormalmap==false) {
for (qint32 srcRow = 0; srcRow < inputArea.height(); ++srcRow) {
do {
const quint8 *data = iterator->oldRawData();
tileRenderer.realheightmap[curPixel] = toDoubleFuncPtr[ki](data, device->colorSpace()->channels()[ki]->pos());
curPixel++;
}
while (iterator->nextPixel());
iterator->nextRow();
}
if (progressUpdater) progressUpdater->setProgress(50);
const int tileHeightMinus1 = inputArea.height() - 1;
const int tileWidthMinus1 = inputArea.width() - 1;
// Foreach INNER pixel in tile
for (int y = 1; y < tileHeightMinus1; ++y) {
for (int x = 1; x < tileWidthMinus1; ++x) {
posup = (y + 1) * inputArea.width() + x;
posdown = (y - 1) * inputArea.width() + x;
posleft = y * inputArea.width() + x - 1;
posright = y * inputArea.width() + x + 1;
memcpy(bumpmapDataPointer,
tileRenderer.IlluminatePixelFromHeightmap(posup, posdown, posleft, posright).data(),
pixelSize);
bumpmapDataPointer += pixelSize;
}
}
} else {
for (qint32 srcRow = 0; srcRow < inputArea.height(); ++srcRow) {
do {
const quint8 *data = iterator->oldRawData();
tileRenderer.realheightmap[curPixel] = toDoubleFuncPtr[ki](data, device->colorSpace()->channels()[ki]->pos());
QVector <float> current_pixel_values(4);
device->colorSpace()->normalisedChannelsValue(data, current_pixel_values );
- //qDebug()<< "Vector:" << current_pixel_values[2] << "," << current_pixel_values[1] << "," << current_pixel_values[0];
+ //dbgKrita<< "Vector:" << current_pixel_values[2] << "," << current_pixel_values[1] << "," << current_pixel_values[0];
memcpy(bumpmapDataPointer,
tileRenderer.IlluminatePixelFromNormalmap(current_pixel_values[2], current_pixel_values[1], current_pixel_values[0]).data(),
pixelSize);
curPixel++;
//pointer that crashes here, but not in the other if statement.
bumpmapDataPointer += pixelSize;
}
while (iterator->nextPixel());
iterator->nextRow();
}
}
if (progressUpdater) progressUpdater->setProgress(90);
KisPaintDeviceSP bumpmapPaintDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb16());
bumpmapPaintDevice->writeBytes(bumpmap.data(), outputArea.x(), outputArea.y(), outputArea.width(), outputArea.height());
KUndo2Command *leaker = bumpmapPaintDevice->convertTo(device->colorSpace(), KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
KisPainter copier(device);
copier.bitBlt(outputArea.x(), outputArea.y(), bumpmapPaintDevice,
outputArea.x(), outputArea.y(), outputArea.width(), outputArea.height());
//device->prepareClone(bumpmapPaintDevice);
//device->makeCloneFrom(bumpmapPaintDevice, bumpmapPaintDevice->extent()); // THIS COULD BE BUG GY
delete leaker;
if (progressUpdater) progressUpdater->setProgress(100);
}
KisFilterConfiguration *KisFilterPhongBumpmap::factoryConfiguration(const KisPaintDeviceSP) const
{
KisFilterConfiguration *config = new KisFilterConfiguration(id(), 2);
config->setProperty(PHONG_AMBIENT_REFLECTIVITY, 0.2);
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY, 0.5);
config->setProperty(PHONG_SPECULAR_REFLECTIVITY, 0.3);
config->setProperty(PHONG_SHINYNESS_EXPONENT, 2);
config->setProperty(USE_NORMALMAP_IS_ENABLED, false);
config->setProperty(PHONG_DIFFUSE_REFLECTIVITY_IS_ENABLED, true);
config->setProperty(PHONG_SPECULAR_REFLECTIVITY_IS_ENABLED, true);
// Indexes are off by 1 simply because arrays start at 0 and the GUI naming scheme started at 1
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[0], true);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[1], true);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[2], false);
config->setProperty(PHONG_ILLUMINANT_IS_ENABLED[3], false);
config->setProperty(PHONG_ILLUMINANT_COLOR[0], QColor(255, 255, 0));
config->setProperty(PHONG_ILLUMINANT_COLOR[1], QColor(255, 0, 0));
config->setProperty(PHONG_ILLUMINANT_COLOR[2], QColor(0, 0, 255));
config->setProperty(PHONG_ILLUMINANT_COLOR[3], QColor(0, 255, 0));
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[0], 50);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[1], 100);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[2], 150);
config->setProperty(PHONG_ILLUMINANT_AZIMUTH[3], 200);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[0], 25);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[1], 20);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[2], 30);
config->setProperty(PHONG_ILLUMINANT_INCLINATION[3], 40);
return config;
}
QRect KisFilterPhongBumpmap::neededRect(const QRect &rect, const KisFilterConfiguration* /*config*/) const
{
return rect.adjusted(-1, -1, 1, 1);
}
QRect KisFilterPhongBumpmap::changedRect(const QRect &rect, const KisFilterConfiguration* /*config*/) const
{
return rect;
}
KisConfigWidget *KisFilterPhongBumpmap::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const
{
KisPhongBumpmapConfigWidget *w = new KisPhongBumpmapConfigWidget(dev, parent);
return w;
}
diff --git a/krita/plugins/filters/tests/kis_all_filter_test.cpp b/krita/plugins/filters/tests/kis_all_filter_test.cpp
index 273bd787bcc..ba5ccfca6e9 100644
--- a/krita/plugins/filters/tests/kis_all_filter_test.cpp
+++ b/krita/plugins/filters/tests/kis_all_filter_test.cpp
@@ -1,308 +1,308 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_all_filter_test.h"
#include <qtest_kde.h>
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "filter/kis_filter.h"
#include "kis_pixel_selection.h"
#include "kis_transaction.h"
#include <KoColorSpaceRegistry.h>
bool compareQImages(QPoint & pt, const QImage & image1, const QImage & image2)
{
// QTime t;
// t.start();
int w1 = image1.width();
int h1 = image1.height();
int w2 = image2.width();
int h2 = image2.height();
if (w1 != w2 || h1 != h2) {
- qDebug() << w1 << " " << w2 << " " << h1 << " " << h2;
+ dbgKrita << w1 << " " << w2 << " " << h1 << " " << h2;
pt.setX(-1);
pt.setY(-1);
return false;
}
for (int x = 0; x < w1; ++x) {
for (int y = 0; y < h1; ++y) {
if (image1.pixel(x, y) != image2.pixel(x, y)) {
pt.setX(x);
pt.setY(y);
return false;
}
}
}
-// qDebug() << "compareQImages time elapsed:" << t.elapsed();
+// dbgKrita << "compareQImages time elapsed:" << t.elapsed();
return true;
}
bool testFilterSrcNotIsDev(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
KisPaintDeviceSP dstdev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
KisFilterConfiguration * kfc = f->defaultConfiguration(dev);
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- //qDebug() << "creating new file for " << f->id();
+ //dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
s = in.readAll();
- //qDebug() << "Read for " << f->id() << "\n" << s;
+ //dbgKrita << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
- qDebug() << f->id();// << "\n" << kfc->toXML() << "\n";
+ dbgKrita << f->id();// << "\n" << kfc->toXML() << "\n";
f->process(dev, dstdev, 0, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
if (!compareQImages(errpoint, result, dstdev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("src_not_is_dst_lena_%1.png").arg(f->id()));
return false;
}
return true;
}
bool testFilterNoTransaction(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
KisFilterConfiguration * kfc = f->defaultConfiguration(dev);
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- //qDebug() << "creating new file for " << f->id();
+ //dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
s = in.readAll();
- //qDebug() << "Read for " << f->id() << "\n" << s;
+ //dbgKrita << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
- qDebug() << f->id();// << "\n" << kfc->toXML() << "\n";
+ dbgKrita << f->id();// << "\n" << kfc->toXML() << "\n";
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("no_transactio_lena_%1.png").arg(f->id()));
return false;
}
return true;
}
bool testFilter(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
QString resultFileName = QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png";
QImage result(resultFileName);
if (!QFileInfo(resultFileName).exists()) {
- qDebug() << resultFileName << " not found";
+ dbgKrita << resultFileName << " not found";
return false;
}
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
KisTransaction * cmd = new KisTransaction(kundo2_noi18n(f->name()), dev);
// Get the predefined configuration from a file
KisFilterConfiguration * kfc = f->defaultConfiguration(dev);
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- //qDebug() << "creating new file for " << f->id();
+ //dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
s = in.readAll();
- //qDebug() << "Read for " << f->id() << "\n" << s;
+ //dbgKrita << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
- qDebug() << f->id();// << "\n" << kfc->toXML() << "\n";
+ dbgKrita << f->id();// << "\n" << kfc->toXML() << "\n";
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
delete cmd;
if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
- qDebug() << errpoint;
+ dbgKrita << errpoint;
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("lena_%1.png").arg(f->id()));
return false;
}
return true;
}
bool testFilterWithSelections(KisFilterSP f)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "lena_" + f->id() + ".png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
KisFilterConfiguration * kfc = f->defaultConfiguration(dev);
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- //qDebug() << "creating new file for " << f->id();
+ //dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
s = in.readAll();
- //qDebug() << "Read for " << f->id() << "\n" << s;
+ //dbgKrita << "Read for " << f->id() << "\n" << s;
kfc->fromXML(s);
}
- qDebug() << f->id();// << "\n"; << kfc->toXML() << "\n";
+ dbgKrita << f->id();// << "\n"; << kfc->toXML() << "\n";
KisSelectionSP sel1 = new KisSelection(new KisSelectionDefaultBounds(dev));
sel1->pixelSelection()->select(qimage.rect());
f->process(dev, dev, sel1, QRect(QPoint(0,0), qimage.size()), kfc);
QPoint errpoint;
if (!compareQImages(errpoint, result, dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()))) {
dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()).save(QString("sel_lena_%1.png").arg(f->id()));
return false;
}
return true;
}
void KisAllFilterTest::testAllFilters()
{
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
qSort(filterList);
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (testFilter(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
- qDebug() << "Success: " << successes;
+ dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
void KisAllFilterTest::testAllFiltersNoTransaction()
{
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
qSort(filterList);
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (testFilterNoTransaction(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
- qDebug() << "Success (no transaction): " << successes;
+ dbgKrita << "Success (no transaction): " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters (no transaction):\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
void KisAllFilterTest::testAllFiltersSrcNotIsDev()
{
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
qSort(filterList);
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (testFilterSrcNotIsDev(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
- qDebug() << "Src!=Dev Success: " << successes;
+ dbgKrita << "Src!=Dev Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Src!=Dev Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
void KisAllFilterTest::testAllFiltersWithSelections()
{
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
qSort(filterList);
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (testFilterWithSelections(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
- qDebug() << "Success: " << successes;
+ dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters with selections:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
QTEST_KDEMAIN(KisAllFilterTest, GUI)
diff --git a/krita/plugins/filters/tests/kis_crash_filter_test.cpp b/krita/plugins/filters/tests/kis_crash_filter_test.cpp
index 0bf2c158ce0..ef2f900bf15 100644
--- a/krita/plugins/filters/tests/kis_crash_filter_test.cpp
+++ b/krita/plugins/filters/tests/kis_crash_filter_test.cpp
@@ -1,97 +1,97 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_crash_filter_test.h"
#include <KoColorProfile.h>
#include <qtest_kde.h>
#include "filter/kis_filter_configuration.h"
#include "filter/kis_filter_registry.h"
#include "kis_selection.h"
#include "kis_processing_information.h"
#include "filter/kis_filter.h"
#include "kis_pixel_selection.h"
#include <KoColorSpaceRegistry.h>
bool KisCrashFilterTest::applyFilter(const KoColorSpace * cs, KisFilterSP f)
{
QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
KisPaintDeviceSP dev = new KisPaintDevice(cs);
// dev->fill(0, 0, 100, 100, dev->defaultPixel());
dev->convertFromQImage(qimage, 0, 0, 0);
// Get the predefined configuration from a file
KisFilterConfiguration * kfc = f->defaultConfiguration(dev);
QFile file(QString(FILES_DATA_DIR) + QDir::separator() + f->id() + ".cfg");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- qDebug() << "creating new file for " << f->id();
+ dbgKrita << "creating new file for " << f->id();
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << kfc->toXML();
} else {
QString s;
QTextStream in(&file);
s = in.readAll();
kfc->fromXML(s);
}
- qDebug() << f->id() << ", " << cs->id() << ", " << cs->profile()->name();// << kfc->toXML() << "\n";
+ dbgKrita << f->id() << ", " << cs->id() << ", " << cs->profile()->name();// << kfc->toXML() << "\n";
f->process(dev, QRect(QPoint(0,0), qimage.size()), kfc);
return true;
}
bool KisCrashFilterTest::testFilter(KisFilterSP f)
{
QList<const KoColorSpace*> colorSpaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::AllProfiles);
bool ok = false;
foreach(const KoColorSpace* colorSpace, colorSpaces) {
// XXX: Let's not check the painterly colorspaces right now
if (colorSpace->id().startsWith("KS", Qt::CaseInsensitive)) {
continue;
}
ok = applyFilter(colorSpace, f);
}
return ok;
}
void KisCrashFilterTest::testCrashFilters()
{
QStringList failures;
QStringList successes;
QList<QString> filterList = KisFilterRegistry::instance()->keys();
qSort(filterList);
for (QList<QString>::Iterator it = filterList.begin(); it != filterList.end(); ++it) {
if (testFilter(KisFilterRegistry::instance()->value(*it)))
successes << *it;
else
failures << *it;
}
- qDebug() << "Success: " << successes;
+ dbgKrita << "Success: " << successes;
if (failures.size() > 0) {
QFAIL(QString("Failed filters:\n\t %1").arg(failures.join("\n\t")).toLatin1());
}
}
QTEST_KDEMAIN(KisCrashFilterTest, GUI)
diff --git a/krita/plugins/formats/exr/exr_converter.cc b/krita/plugins/formats/exr/exr_converter.cc
index a1e6d446632..ed252a91cc6 100644
--- a/krita/plugins/formats/exr/exr_converter.cc
+++ b/krita/plugins/formats/exr/exr_converter.cc
@@ -1,1264 +1,1264 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "exr_converter.h"
#include <half.h>
#include <ImfAttribute.h>
#include <ImfChannelList.h>
#include <ImfInputFile.h>
#include <ImfOutputFile.h>
#include <ImfStringAttribute.h>
#include "exr_extra_tags.h"
#include <QApplication>
#include <QMessageBox>
#include <kurl.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoColorModelStandardIds.h>
#include <KoColor.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_transaction.h>
#include "kis_iterator_ng.h"
#include "kra/kis_kra_savexml_visitor.h"
#include <kis_exr_layers_sorter.h>
#include <metadata/kis_meta_data_entry.h>
#include <metadata/kis_meta_data_schema.h>
#include <metadata/kis_meta_data_schema_registry.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_value.h>
template<typename _T_>
struct Rgba {
_T_ r;
_T_ g;
_T_ b;
_T_ a;
};
struct ExrGroupLayerInfo;
struct ExrLayerInfoBase {
ExrLayerInfoBase() : colorSpace(0), parent(0) {
}
const KoColorSpace* colorSpace;
QString name;
const ExrGroupLayerInfo* parent;
};
struct ExrGroupLayerInfo : public ExrLayerInfoBase {
ExrGroupLayerInfo() : groupLayer(0) {}
KisGroupLayerSP groupLayer;
};
enum ImageType {
IT_UNKNOWN,
IT_FLOAT16,
IT_FLOAT32,
IT_UNSUPPORTED
};
struct ExrPaintLayerInfo : public ExrLayerInfoBase {
ExrPaintLayerInfo() : imageType(IT_UNKNOWN) {
}
ImageType imageType;
QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name
struct Remap {
Remap(const QString& _original, const QString& _current) : original(_original), current(_current) {
}
QString original;
QString current;
};
QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita
void updateImageType(ImageType channelType);
};
void ExrPaintLayerInfo::updateImageType(ImageType channelType)
{
if (imageType == IT_UNKNOWN) {
imageType = channelType;
} else if (imageType != channelType) {
imageType = IT_UNSUPPORTED;
}
}
struct ExrPaintLayerSaveInfo {
QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.")
KisPaintLayerSP layer;
QList<QString> channels;
Imf::PixelType pixelType;
};
struct exrConverter::Private {
Private() : doc(0), warnedAboutChangedAlpha(false),
showNotifications(false) {}
KisImageSP image;
KisDocument *doc;
bool warnedAboutChangedAlpha;
bool showNotifications;
template <class WrapperType>
void unmultiplyAlpha(typename WrapperType::pixel_type *pixel);
template<typename _T_>
void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
template<typename _T_>
void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
QDomDocument loadExtraLayersInfo(const Imf::Header &header);
bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames);
void makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects);
void recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent);
void reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved);
QString fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects);
};
exrConverter::exrConverter(KisDocument *doc, bool showNotifications)
: m_d(new Private)
{
m_d->doc = doc;
m_d->showNotifications = showNotifications;
}
exrConverter::~exrConverter()
{
}
ImageType imfTypeToKisType(Imf::PixelType type)
{
switch (type) {
case Imf::UINT:
case Imf::NUM_PIXELTYPES:
return IT_UNSUPPORTED;
case Imf::HALF:
return IT_FLOAT16;
case Imf::FLOAT:
return IT_FLOAT32;
default:
qFatal("Out of bound enum");
return IT_UNKNOWN;
}
}
const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType)
{
switch (imageType) {
case IT_FLOAT16:
return KoColorSpaceRegistry::instance()->colorSpace(model, Float16BitsColorDepthID.id(), "");
case IT_FLOAT32:
return KoColorSpaceRegistry::instance()->colorSpace(model, Float32BitsColorDepthID.id(), "");
case IT_UNKNOWN:
case IT_UNSUPPORTED:
return 0;
default:
qFatal("Out of bound enum");
return 0;
}
}
template <typename T>
static inline T alphaEpsilon()
{
return static_cast<T>(HALF_EPSILON);
}
template <typename T>
static inline T alphaNoiseThreshold()
{
return static_cast<T>(0.01); // 1%
}
template <typename T>
struct RgbPixelWrapper
{
typedef T channel_type;
typedef Rgba<T> pixel_type;
RgbPixelWrapper(Rgba<T> &_pixel) : pixel(_pixel) {}
inline T alpha() const {
return pixel.a;
}
inline bool checkMultipliedColorsConsistent() const {
return !(pixel.a < alphaEpsilon<T>() &&
(pixel.r > 0.0 ||
pixel.g > 0.0 ||
pixel.b > 0.0));
}
inline bool checkUnmultipliedColorsConsistent(const Rgba<T> &mult) const {
const T alpha = pixel.a;
return alpha >= alphaNoiseThreshold<T>() ||
(pixel.r * alpha == mult.r &&
pixel.g * alpha == mult.g &&
pixel.b * alpha == mult.b);
}
inline void setUnmultiplied(const Rgba<T> &mult, qreal newAlpha) {
pixel.r = mult.r / newAlpha;
pixel.g = mult.g / newAlpha;
pixel.b = mult.b / newAlpha;
pixel.a = newAlpha;
}
Rgba<T> &pixel;
};
template <typename T>
struct GrayPixelWrapper
{
typedef T channel_type;
typedef typename KoGrayTraits<T>::Pixel pixel_type;
GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {}
inline T alpha() const {
return pixel.alpha;
}
inline bool checkMultipliedColorsConsistent() const {
return !(pixel.alpha < alphaEpsilon<T>() &&
pixel.gray > 0.0);
}
inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const {
const T alpha = pixel.alpha;
return alpha >= alphaNoiseThreshold<T>() ||
pixel.gray * alpha == mult.gray;
}
inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) {
pixel.gray = mult.gray / newAlpha;
pixel.alpha = newAlpha;
}
pixel_type &pixel;
};
template <class WrapperType>
void exrConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel)
{
typedef typename WrapperType::pixel_type pixel_type;
typedef typename WrapperType::channel_type channel_type;
WrapperType srcPixel(*pixel);
if (!srcPixel.checkMultipliedColorsConsistent()) {
bool alphaWasModified = false;
channel_type newAlpha = srcPixel.alpha();
pixel_type __dstPixelData;
WrapperType dstPixel(__dstPixelData);
/**
* Division by a tiny alpha may result in an overflow of half
* value. That is why we use safe iterational approach.
*/
while (1) {
dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha);
if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) {
break;
}
newAlpha += alphaEpsilon<channel_type>();
alphaWasModified = true;
}
*pixel = dstPixel.pixel;
if (alphaWasModified &&
!this->warnedAboutChangedAlpha) {
QString msg =
i18nc("@info",
"The image contains pixels with zero alpha channel and non-zero "
"color channels. Krita will have to modify those pixels to have "
"at least some alpha. The initial values will <emphasis>not</emphasis> "
"be reverted on saving the image back."
"<nl/><nl/>"
"This will hardly make any visual difference just keep it in mind."
"<nl/><nl/>"
"<note>Modified alpha will have a range from <numid>%1</numid> to <numid>%2</numid></note>",
alphaEpsilon<channel_type>(),
alphaNoiseThreshold<channel_type>());
if (this->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "EXR image will be modified"), msg);
} else {
- qWarning() << "WARNING:" << msg;
+ warnKrita << "WARNING:" << msg;
}
this->warnedAboutChangedAlpha = true;
}
} else if (srcPixel.alpha() > 0.0) {
srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha());
}
}
template <typename T, typename Pixel, int size, int alphaPos>
void multiplyAlpha(Pixel *pixel)
{
T alpha = pixel->data[alphaPos];
if (alpha > 0.0) {
for (int i = 0; i < size; ++i) {
if (i != alphaPos) {
pixel->data[i] *= alpha;
}
}
pixel->data[alphaPos] = alpha;
}
}
template<typename _T_>
void exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
{
typedef Rgba<_T_> Rgba;
QVector<Rgba> pixels(width);
bool hasAlpha = info.channelMap.contains("A");
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
frameBuffer.insert(info.channelMap["R"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->r,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->g,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["B"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->b,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->a,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
}
file.setFrameBuffer(frameBuffer);
file.readPixels(ystart + y);
Rgba *rgba = pixels.data();
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width);
do {
if (hasAlpha) {
unmultiplyAlpha<RgbPixelWrapper<_T_> >(rgba);
}
typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast<typename KoRgbTraits<_T_>::Pixel*>(it->rawData());
dst->red = rgba->r;
dst->green = rgba->g;
dst->blue = rgba->b;
if (hasAlpha) {
dst->alpha = rgba->a;
} else {
dst->alpha = 1.0;
}
++rgba;
} while (it->nextPixel());
}
}
template<typename _T_>
void exrConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
{
typedef typename GrayPixelWrapper<_T_>::channel_type channel_type;
typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
KIS_ASSERT_RECOVER_RETURN(
layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
QVector<pixel_type> pixels(width);
Q_ASSERT(info.channelMap.contains("G"));
dbgFile << "G -> " << info.channelMap["G"];
bool hasAlpha = info.channelMap.contains("A");
dbgFile << "Has Alpha:" << hasAlpha;
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->gray,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->alpha,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
}
file.setFrameBuffer(frameBuffer);
file.readPixels(ystart + y);
pixel_type *srcPtr = pixels.data();
KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, y, width);
do {
if (hasAlpha) {
unmultiplyAlpha<GrayPixelWrapper<_T_> >(srcPtr);
}
pixel_type* dstPtr = reinterpret_cast<pixel_type*>(it->rawData());
dstPtr->gray = srcPtr->gray;
dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0);
++srcPtr;
} while (it->nextPixel());
}
}
bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2)
{
if (idx1 > idx2) return true;
if (group.name == list[idx2]) {
return recCheckGroup(*group.parent, list, idx1, idx2 - 1);
}
return false;
}
ExrGroupLayerInfo* searchGroup(QList<ExrGroupLayerInfo>* groups, QStringList list, int idx1, int idx2)
{
if (idx1 > idx2) {
return 0;
}
// Look for the group
for (int i = 0; i < groups->size(); ++i) {
if (recCheckGroup(groups->at(i), list, idx1, idx2)) {
return &(*groups)[i];
}
}
// Create the group
ExrGroupLayerInfo info;
info.name = list.at(idx2);
info.parent = searchGroup(groups, list, idx1, idx2 - 1);
groups->append(info);
return &groups->last();
}
QDomDocument exrConverter::Private::loadExtraLayersInfo(const Imf::Header &header)
{
const Imf::StringAttribute *layersInfoAttribute =
header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
if (!layersInfoAttribute) return QDomDocument();
QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str());
QDomDocument doc;
doc.setContent(layersInfoString);
return doc;
}
bool exrConverter::Private::checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames)
{
std::set<std::string> extraInfoLayers;
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; };
QDomElement el = root.firstChildElement();
while(!el.isNull()) {
KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; };
extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData());
el = el.nextSiblingElement();
}
bool result = extraInfoLayers == exrLayerNames;
if (!result) {
- qDebug() << "WARINING: Krita EXR extra layers info is inconsistent!";
- qDebug() << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size());
+ dbgKrita << "WARINING: Krita EXR extra layers info is inconsistent!";
+ dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size());
std::set<std::string>::const_iterator it1 = extraInfoLayers.begin();
std::set<std::string>::const_iterator it2 = exrLayerNames.begin();
std::set<std::string>::const_iterator end1 = extraInfoLayers.end();
std::set<std::string>::const_iterator end2 = exrLayerNames.end();
for (; it1 != end1; ++it1, ++it2) {
- qDebug() << it1->c_str() << it2->c_str();
+ dbgKrita << it1->c_str() << it2->c_str();
}
}
return result;
}
KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
{
dbgFile << "Load exr: " << uri << " " << QFile::encodeName(uri.toLocalFile());
Imf::InputFile file(QFile::encodeName(uri.toLocalFile()));
Imath::Box2i dw = file.header().dataWindow();
int width = dw.max.x - dw.min.x + 1;
int height = dw.max.y - dw.min.y + 1;
int dx = dw.min.x;
int dy = dw.min.y;
// Display the attributes of a file
for (Imf::Header::ConstIterator it = file.header().begin();
it != file.header().end(); ++it) {
dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName();
}
// fetch Krita's extra layer info, which might have been stored previously
QDomDocument extraLayersInfo = m_d->loadExtraLayersInfo(file.header());
// Constuct the list of LayerInfo
QList<ExrPaintLayerInfo> informationObjects;
QList<ExrGroupLayerInfo> groups;
ImageType imageType = IT_UNKNOWN;
const Imf::ChannelList &channels = file.header().channels();
std::set<std::string> layerNames;
channels.layers(layerNames);
if (!extraLayersInfo.isNull() &&
!m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
// it is inconsistent anyway
extraLayersInfo = QDomDocument();
}
// Test if it is a multilayer EXR or singlelayer
if (layerNames.empty()) {
dbgFile << "Single layer:";
ExrPaintLayerInfo info;
info.name = i18n("HDR Layer");
for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
const Imf::Channel &channel = i.channel();
dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
info.updateImageType(imfTypeToKisType(channel.type));
QString qname = i.name();
if (qname != "A" && qname != "R" && qname != "G" && qname != "B") {
dbgFile << "Unknow: " << i.name();
info.imageType = IT_UNSUPPORTED;
} else {
info.channelMap[qname] = qname;
}
}
informationObjects.push_back(info);
imageType = info.imageType;
} else {
dbgFile << "Multi layers:";
for (std::set<std::string>::const_iterator i = layerNames.begin();
i != layerNames.end(); ++i) {
ExrPaintLayerInfo info;
dbgFile << "layer name = " << i->c_str();
info.name = i->c_str();
Imf::ChannelList::ConstIterator layerBegin, layerEnd;
channels.channelsInLayer(*i, layerBegin, layerEnd);
for (Imf::ChannelList::ConstIterator j = layerBegin;
j != layerEnd; ++j) {
const Imf::Channel &channel = j.channel();
dbgFile << "\tchannel " << j.name() << " type = " << channel.type;
info.updateImageType(imfTypeToKisType(channel.type));
QString qname = j.name();
QStringList list = qname.split('.');
QString layersuffix = list.last();
if (list.size() > 1) {
info.name = list[list.size()-2];
info.parent = searchGroup(&groups, list, 0, list.size() - 3);
}
info.channelMap[layersuffix] = qname;
}
if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
informationObjects.push_back(info);
if (imageType < info.imageType) {
imageType = info.imageType;
}
}
}
}
dbgFile << "File has " << informationObjects.size() << " layers";
// Set the colorspaces
for (int i = 0; i < informationObjects.size(); ++i) {
ExrPaintLayerInfo& info = informationObjects[i];
QString modelId;
if (info.channelMap.size() == 1) {
modelId = GrayAColorModelID.id();
QString key = info.channelMap.begin().key();
if (key != "G") {
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G"));
QString channel = info.channelMap.begin().value();
info.channelMap.clear();
info.channelMap["G"] = channel;
}
} else if (info.channelMap.size() == 2) {
modelId = GrayAColorModelID.id();
QMap<QString,QString>::iterator it = info.channelMap.begin();
QMap<QString,QString>::iterator end = info.channelMap.end();
QString failingChannelKey;
for (; it != end; ++it) {
if (it.key() != "G" && it.key() != "A") {
failingChannelKey = it.key();
break;
}
}
info.remappedChannels.push_back(
ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
QString failingChannelValue = info.channelMap[failingChannelKey];
info.channelMap.remove(failingChannelKey);
info.channelMap["G"] = failingChannelValue;
} else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) {
if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) {
modelId = RGBAColorModelID.id();
}
else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) {
modelId = XYZAColorModelID.id();
QMap<QString, QString> newChannelMap;
if (info.channelMap.contains("W")) {
newChannelMap["A"] = info.channelMap["W"];
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z"));
} else if (info.channelMap.contains("A")) {
newChannelMap["A"] = info.channelMap["A"];
}
// The decode function expect R, G, B in the channel map
newChannelMap["B"] = info.channelMap["X"];
newChannelMap["G"] = info.channelMap["Y"];
newChannelMap["R"] = info.channelMap["Z"];
info.channelMap = newChannelMap;
}
else {
modelId = RGBAColorModelID.id();
QMap<QString, QString> newChannelMap;
QMap<QString, QString>::iterator it = info.channelMap.begin();
newChannelMap["R"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R"));
++it;
newChannelMap["G"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G"));
++it;
newChannelMap["B"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B"));
if (info.channelMap.size() == 4) {
++it;
newChannelMap["A"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A"));
}
info.channelMap = newChannelMap;
}
}
if (!modelId.isEmpty()) {
info.colorSpace = kisTypeToColorSpace(modelId, info.imageType);
}
}
// Get colorspace
dbgFile << "Image type = " << imageType;
const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType);
if (!colorSpace) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
dbgFile << "Colorspace: " << colorSpace->name();
// Set the colorspace on all groups
for (int i = 0; i < groups.size(); ++i) {
ExrGroupLayerInfo& info = groups[i];
info.colorSpace = colorSpace;
}
// Create the image
m_d->image = new KisImage(m_d->doc->createUndoStore(), width, height, colorSpace, "");
if (!m_d->image) {
return KisImageBuilder_RESULT_FAILURE;
}
/**
* EXR semi-transparent images are expected to be rendered on
* black to ensure correctness of the light model
*/
m_d->image->setDefaultProjectionColor(KoColor(Qt::black, colorSpace));
// Create group layers
for (int i = 0; i < groups.size(); ++i) {
ExrGroupLayerInfo& info = groups[i];
Q_ASSERT(info.parent == 0 || info.parent->groupLayer);
KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_d->image->rootLayer();
info.groupLayer = new KisGroupLayer(m_d->image, info.name, OPACITY_OPAQUE_U8);
m_d->image->addNode(info.groupLayer, groupLayerParent);
}
// Load the layers
for (int i = 0; i < informationObjects.size(); ++i) {
ExrPaintLayerInfo& info = informationObjects[i];
if (info.colorSpace) {
dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id();
KisPaintLayerSP layer = new KisPaintLayer(m_d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace);
layer->setCompositeOp(COMPOSITE_OVER);
if (!layer) {
return KisImageBuilder_RESULT_FAILURE;
}
switch (info.channelMap.size()) {
case 1:
case 2:
// Decode the data
switch (imageType) {
case IT_FLOAT16:
m_d->decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
break;
case IT_FLOAT32:
m_d->decodeData1<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
break;
case IT_UNKNOWN:
case IT_UNSUPPORTED:
qFatal("Impossible error");
}
break;
case 3:
case 4:
// Decode the data
switch (imageType) {
case IT_FLOAT16:
m_d->decodeData4<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
break;
case IT_FLOAT32:
m_d->decodeData4<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
break;
case IT_UNKNOWN:
case IT_UNSUPPORTED:
qFatal("Impossible error");
}
break;
default:
qFatal("Invalid number of channels: %i", info.channelMap.size());
}
// Check if should set the channels
if (!info.remappedChannels.isEmpty()) {
QList<KisMetaData::Value> values;
foreach(const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) {
QMap<QString, KisMetaData::Value> map;
map["original"] = KisMetaData::Value(remap.original);
map["current"] = KisMetaData::Value(remap.current);
values.append(map);
}
layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values));
}
// Add the layer
KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : m_d->image->rootLayer();
m_d->image->addNode(layer, groupLayerParent);
} else {
dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space";
}
}
if (!extraLayersInfo.isNull()) {
KisExrLayersSorter sorter(extraLayersInfo, m_d->image);
}
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result exrConverter::buildImage(const KUrl& uri)
{
if (uri.isEmpty())
return KisImageBuilder_RESULT_NO_URI;
if (!uri.isLocalFile()) {
return KisImageBuilder_RESULT_NOT_EXIST;
}
return decode(uri);
}
KisImageWSP exrConverter::image()
{
return m_d->image;
}
template<typename _T_, int size>
struct ExrPixel_ {
_T_ data[size];
};
class Encoder
{
public:
virtual ~Encoder() {}
virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0;
virtual void encodeData(int line) = 0;
};
template<typename _T_, int size, int alphaPos>
class EncoderImpl : public Encoder
{
public:
EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {}
virtual ~EncoderImpl() {}
virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line);
virtual void encodeData(int line);
private:
typedef ExrPixel_<_T_, size> ExrPixel;
Imf::OutputFile* file;
const ExrPaintLayerSaveInfo* info;
QVector<ExrPixel> pixels;
int m_width;
};
template<typename _T_, int size, int alphaPos>
void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line)
{
int xstart = 0;
int ystart = 0;
ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width;
for (int k = 0; k < size; ++k) {
frameBuffer->insert(info->channels[k].toUtf8(),
Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k],
sizeof(ExrPixel) * 1,
sizeof(ExrPixel) * m_width));
}
}
template<typename _T_, int size, int alphaPos>
void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
{
ExrPixel *rgba = pixels.data();
KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width);
do {
const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData());
for (int i = 0; i < size; ++i) {
rgba->data[i] = dst[i];
}
if (alphaPos != -1) {
multiplyAlpha<_T_, ExrPixel, size, alphaPos>(rgba);
}
++rgba;
} while (it->nextPixel());
}
Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width)
{
// bool hasAlpha = info.layer->colorSpace()->channelCount() != info.layer->colorSpace()->colorChannelCount();
switch (info.layer->colorSpace()->channelCount()) {
case 1: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl < half, 1, -1 > (&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl < float, 1, -1 > (&file, &info, width);
}
break;
}
case 2: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 2, 1>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 2, 1>(&file, &info, width);
}
break;
}
case 4: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 4, 3>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 4, 3>(&file, &info, width);
}
break;
}
default:
qFatal("Impossible error");
}
return 0;
}
void encodeData(Imf::OutputFile& file, const QList<ExrPaintLayerSaveInfo>& informationObjects, int width, int height)
{
QList<Encoder*> encoders;
foreach(const ExrPaintLayerSaveInfo& info, informationObjects) {
encoders.push_back(encoder(file, info, width));
}
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
foreach(Encoder* encoder, encoders) {
encoder->prepareFrameBuffer(&frameBuffer, y);
}
file.setFrameBuffer(frameBuffer);
foreach(Encoder* encoder, encoders) {
encoder->encodeData(y);
}
file.writePixels(1);
}
qDeleteAll(encoders);
}
KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, KisPaintLayerSP layer)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
KisImageWSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
if (uri.isEmpty())
return KisImageBuilder_RESULT_NO_URI;
if (!uri.isLocalFile())
return KisImageBuilder_RESULT_NOT_LOCAL;
// Make the header
qint32 height = image->height();
qint32 width = image->width();
Imf::Header header(width, height);
Imf::PixelType pixelType = Imf::NUM_PIXELTYPES;
if(layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
pixelType = Imf::HALF;
} else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID)
{
pixelType = Imf::FLOAT;
}
if(pixelType >= Imf::NUM_PIXELTYPES)
{
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
header.channels().insert("R", Imf::Channel(pixelType));
header.channels().insert("G", Imf::Channel(pixelType));
header.channels().insert("B", Imf::Channel(pixelType));
header.channels().insert("A", Imf::Channel(pixelType));
ExrPaintLayerSaveInfo info;
info.layer = layer;
info.channels.push_back("R");
info.channels.push_back("G");
info.channels.push_back("B");
info.channels.push_back("A");
info.pixelType = pixelType;
// Open file for writing
Imf::OutputFile file(QFile::encodeName(uri.path()), header);
QList<ExrPaintLayerSaveInfo> informationObjects;
informationObjects.push_back(info);
encodeData(file, informationObjects, width, height);
return KisImageBuilder_RESULT_OK;
}
QString remap(const QMap<QString, QString>& current2original, const QString& current)
{
if (current2original.contains(current)) {
return current2original[current];
}
return current;
}
void exrConverter::Private::makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects)
{
typedef QMultiMap<QString, QList<ExrPaintLayerSaveInfo>::iterator> NamesMap;
NamesMap namesMap;
{
QList<ExrPaintLayerSaveInfo>::iterator it = informationObjects.begin();
QList<ExrPaintLayerSaveInfo>::iterator end = informationObjects.end();
for (; it != end; ++it) {
namesMap.insert(it->name, it);
}
}
foreach (const QString &key, namesMap.keys()) {
if (namesMap.count(key) > 1) {
KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; }
QString strippedName = key.left(key.size() - 1); // trim the ending dot
int nameCounter = 0;
NamesMap::iterator it = namesMap.find(key);
NamesMap::iterator end = namesMap.end();
for (; it != end; ++it) {
QString newName =
QString("%1_%2.")
.arg(strippedName)
.arg(nameCounter++);
it.value()->name = newName;
QList<QString>::iterator channelsIt = it.value()->channels.begin();
QList<QString>::iterator channelsEnd = it.value()->channels.end();
for (; channelsIt != channelsEnd; ++channelsIt) {
channelsIt->replace(key, newName);
}
}
}
}
}
void exrConverter::Private::recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent)
{
QSet<KisNodeSP> layersNotSaved;
for (uint i = 0; i < parent->childCount(); ++i) {
KisNodeSP node = parent->at(i);
if (KisPaintLayerSP paintLayer = dynamic_cast<KisPaintLayer*>(node.data())) {
QMap<QString, QString> current2original;
if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) {
const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap");
QList< KisMetaData::Value> values = entry.value().asArray();
foreach(const KisMetaData::Value& value, values) {
QMap<QString, KisMetaData::Value> map = value.asStructure();
if (map.contains("original") && map.contains("current")) {
current2original[map["current"].toString()] = map["original"].toString();
}
}
}
ExrPaintLayerSaveInfo info;
info.name = name + paintLayer->name() + '.';
info.layer = paintLayer;
if (paintLayer->colorSpace()->colorModelId() == RGBAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "R"));
info.channels.push_back(info.name + remap(current2original, "G"));
info.channels.push_back(info.name + remap(current2original, "B"));
info.channels.push_back(info.name + remap(current2original, "A"));
} else if (paintLayer->colorSpace()->colorModelId() == GrayAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "G"));
info.channels.push_back(info.name + remap(current2original, "A"));
} else if (paintLayer->colorSpace()->colorModelId() == GrayColorModelID) {
info.channels.push_back(info.name + remap(current2original, "G"));
} else if (paintLayer->colorSpace()->colorModelId() == XYZAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "X"));
info.channels.push_back(info.name + remap(current2original, "Y"));
info.channels.push_back(info.name + remap(current2original, "Z"));
info.channels.push_back(info.name + remap(current2original, "A"));
}
if (paintLayer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
info.pixelType = Imf::HALF;
} else if (paintLayer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
info.pixelType = Imf::FLOAT;
} else {
info.pixelType = Imf::NUM_PIXELTYPES;
}
if(info.pixelType < Imf::NUM_PIXELTYPES)
{
informationObjects.push_back(info);
} else {
// TODO should probably inform that one of the layer cannot be saved.
}
} else if (KisGroupLayerSP groupLayer = dynamic_cast<KisGroupLayer*>(node.data())) {
recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer);
} else {
/**
* The EXR can store paint and group layers only. The rest will
* go to /dev/null :(
*/
layersNotSaved.insert(node);
}
}
if (!layersNotSaved.isEmpty()) {
reportLayersNotSaved(layersNotSaved);
}
}
void exrConverter::Private::reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved)
{
QString layersList;
QTextStream textStream(&layersList);
foreach(KisNodeSP node, layersNotSaved) {
textStream << "<item>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</item>";
}
QString msg =
i18nc("@info",
"<para>The following layers have a type that is not supported by EXR format:</para>"
"<para><list>%1</list></para>"
"<para><warning>these layers will NOT be saved to the final EXR file</warning></para>", layersList);
if (this->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "Layers will be lost"), msg);
} else {
- qWarning() << "WARNING:" << msg;
+ warnKrita << "WARNING:" << msg;
}
}
QString exrConverter::Private::fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects)
{
KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty());
QDomDocument doc("krita-extra-layers-info");
doc.appendChild(doc.createElement("root"));
QDomElement rootElement = doc.documentElement();
for (int i = 0; i < informationObjects.size(); i++) {
ExrPaintLayerSaveInfo &info = informationObjects[i];
quint32 unused;
KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false);
QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc);
// cut the ending '.'
QString strippedName = info.name.left(info.name.size() - 1);
el.setAttribute(EXR_NAME, strippedName);
rootElement.appendChild(el);
}
return doc.toString();
}
KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, KisGroupLayerSP layer)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
KisImageWSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
if (uri.isEmpty())
return KisImageBuilder_RESULT_NO_URI;
if (!uri.isLocalFile())
return KisImageBuilder_RESULT_NOT_LOCAL;
qint32 height = image->height();
qint32 width = image->width();
Imf::Header header(width, height);
QList<ExrPaintLayerSaveInfo> informationObjects;
m_d->recBuildPaintLayerSaveInfo(informationObjects, "", layer);
if(informationObjects.isEmpty()) {
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
m_d->makeLayerNamesUnique(informationObjects);
QByteArray extraLayersInfo = m_d->fetchExtraLayersInfo(informationObjects).toUtf8();
header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData()));
dbgFile << informationObjects.size() << " layers to save";
foreach(const ExrPaintLayerSaveInfo& info, informationObjects) {
if (info.pixelType < Imf::NUM_PIXELTYPES) {
foreach(const QString& channel, info.channels) {
dbgFile << channel << " " << info.pixelType;
header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType));
}
}
}
// Open file for writing
Imf::OutputFile file(QFile::encodeName(uri.path()), header);
encodeData(file, informationObjects, width, height);
return KisImageBuilder_RESULT_OK;
}
void exrConverter::cancel()
{
- qWarning() << "WARNING: Cancelling of an EXR loading is not supported!";
+ warnKrita << "WARNING: Cancelling of an EXR loading is not supported!";
}
diff --git a/krita/plugins/formats/odg/kis_odg_import.cc b/krita/plugins/formats/odg/kis_odg_import.cc
index 62afa3f4527..1954cd99e40 100644
--- a/krita/plugins/formats/odg/kis_odg_import.cc
+++ b/krita/plugins/formats/odg/kis_odg_import.cc
@@ -1,157 +1,157 @@
/*
* Copyright (c) 2006-2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2007 Thomas Zander <zander@kde.org>
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_odg_import.h"
#include <kpluginfactory.h>
#include <KisFilterChain.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <KisViewManager.h>
#include <kis_shape_layer.h>
#include <KoOdfReadStore.h>
#include <KoOdfLoadingContext.h>
#include <KoShapeLoadingContext.h>
#include <KoXmlNS.h>
#include <KoShapeRegistry.h>
#include <KoShapeBasedDocumentBase.h>
#include <KoColorSpaceRegistry.h>
K_PLUGIN_FACTORY_WITH_JSON(ODGImportFactory, "krita_odg_import.json", registerPlugin<KisODGImport>();)
KisODGImport::KisODGImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisODGImport::~KisODGImport()
{
}
KisImportExportFilter::ConversionStatus KisODGImport::convert(const QByteArray& from, const QByteArray& to)
{
dbgFile << "Import odg";
if (to != "application/x-krita")
return KisImportExportFilter::BadMimeType;
KisDocument * doc = m_chain->outputDocument();
if (!doc)
return KisImportExportFilter::NoDocumentCreated;
QString filename = m_chain->inputFile();
KoStore* store = KoStore::createStore(filename, KoStore::Read, from, KoStore::Zip);
if (!store || store->bad()) {
delete store;
return KisImportExportFilter::BadConversionGraph;
}
doc -> prepareForImport();
KoOdfReadStore odfStore(store);
QString errorMessage;
odfStore.loadAndParse(errorMessage);
if (!errorMessage.isEmpty()) {
warnKrita << errorMessage;
return KisImportExportFilter::CreationError;
}
KoXmlElement contents = odfStore.contentDoc().documentElement();
KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body"));
if (body.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) );
return KisImportExportFilter::CreationError;
}
body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing");
if (body.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) );
return KisImportExportFilter::CreationError;
}
KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page"));
if (page.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) );
return KisImportExportFilter::CreationError;
}
KoXmlElement * master = 0;
if (odfStore.styles().masterPages().contains("Standard"))
master = odfStore.styles().masterPages().value("Standard");
else if (odfStore.styles().masterPages().contains("Default"))
master = odfStore.styles().masterPages().value("Default");
else if (! odfStore.styles().masterPages().empty())
master = odfStore.styles().masterPages().begin().value();
qint32 width = 1000;
qint32 height = 1000;
if (master) {
const KoXmlElement *style = odfStore.styles().findStyle(
master->attributeNS(KoXmlNS::style, "page-layout-name", QString()));
KoPageLayout pageLayout;
pageLayout.loadOdf(*style);
width = pageLayout.width;
height = pageLayout.height;
}
// We work fine without a master page
KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
context.setManifestFile(QString("tar:/") + odfStore.store()->currentPath() + "META-INF/manifest.xml");
KoShapeLoadingContext shapeContext(context, doc->shapeController()->resourceManager());
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageWSP image = new KisImage(doc->createUndoStore(), width, height, cs, "built image");
doc->setCurrentImage(image);
KoXmlElement layerElement;
forEachElement(layerElement, KoXml::namedItemNS(page, KoXmlNS::draw, "layer-set")) {
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image,
i18n("Vector Layer"),
OPACITY_OPAQUE_U8);
if (!shapeLayer->loadOdf(layerElement, shapeContext)) {
- kWarning() << "Could not load vector layer!";
+ dbgKrita << "Could not load vector layer!";
return KisImportExportFilter::CreationError;
}
image->addNode(shapeLayer, image->rootLayer(), 0);
}
KoXmlElement child;
forEachElement(child, page) {
/*KoShape * shape = */KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext);
}
return KisImportExportFilter::OK;
}
#include "kis_odg_import.moc"
diff --git a/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp b/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
index e52b543551f..c217f757e7c 100644
--- a/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
+++ b/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
@@ -1,413 +1,413 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_additional_layer_info_block.h"
#include <QDomDocument>
#include <asl/kis_offset_on_exit_verifier.h>
#include <asl/kis_asl_reader_utils.h>
#include <asl/kis_asl_reader.h>
#include <asl/kis_asl_writer_utils.h>
#include <asl/kis_asl_writer.h>
#include <asl/kis_asl_patterns_writer.h>
PsdAdditionalLayerInfoBlock::PsdAdditionalLayerInfoBlock(const PSDHeader& header)
: m_header(header)
{
}
void PsdAdditionalLayerInfoBlock::setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
{
m_layerInfoBlockHandler = handler;
}
bool PsdAdditionalLayerInfoBlock::read(QIODevice *io)
{
bool result = true;
try {
readImpl(io);
} catch (KisAslReaderUtils::ASLParseException &e) {
error = e.what();
result = false;
}
return result;
}
void PsdAdditionalLayerInfoBlock::readImpl(QIODevice* io)
{
using namespace KisAslReaderUtils;
QStringList longBlocks;
if (m_header.version > 1) {
longBlocks << "LMsk" << "Lr16" << "Layr" << "Mt16" << "Mtrn" << "Alph";
}
while (!io->atEnd()) {
{
const quint32 refSignature1 = 0x3842494D; // '8BIM' in little-endian
const quint32 refSignature2 = 0x38423634; // '8B64' in little-endian
if (!TRY_READ_SIGNATURE_2OPS_EX(io, refSignature1, refSignature2)) {
break;
}
}
QString key = readFixedString(io);
dbgFile << "found info block with key" << key;
quint64 blockSize = GARBAGE_VALUE_MARK;
if (longBlocks.contains(key)) {
SAFE_READ_EX(io, blockSize);
} else {
quint32 size32;
SAFE_READ_EX(io, size32);
blockSize = size32;
}
// offset verifier will correct the position on the exit from
// current namespace, including 'continue', 'return' and
// exceptions.
SETUP_OFFSET_VERIFIER(infoBlockEndVerifier, io, blockSize, 0);
if (keys.contains(key)) {
error = "Found duplicate entry for key ";
continue;
}
keys << key;
// TODO: Loading of 32 bit files is not supported yet
if (key == "Lr16"/* || key == "Lr32"*/) {
if (m_layerInfoBlockHandler) {
int offset = m_header.version > 1 ? 8 : 4;
io->seek(io->pos() - offset);
m_layerInfoBlockHandler(io);
}
}
else if (key == "SoCo") {
}
else if (key == "GdFl") {
}
else if (key == "PtFl") {
}
else if (key == "brit") {
}
else if (key == "levl") {
}
else if (key == "curv") {
}
else if (key == "expA") {
}
else if (key == "vibA") {
}
else if (key == "hue") {
}
else if (key == "hue2") {
}
else if (key == "blnc") {
}
else if (key == "blwh") {
}
else if (key == "phfl") {
}
else if (key == "mixr") {
}
else if (key == "clrL") {
}
else if (key == "nvrt") {
}
else if (key == "post") {
}
else if (key == "thrs") {
}
else if (key == "grdm") {
}
else if (key == "selc") {
}
else if (key == "lrFX") {
// deprecated! use lfx2 instead!
}
else if (key == "tySh") {
}
else if (key == "luni") {
// get the unicode layer name
unicodeLayerName = readUnicodeString(io);
dbgFile << "unicodeLayerName" << unicodeLayerName;
}
else if (key == "lyid") {
}
else if (key == "lfx2") {
KisAslReader reader;
layerStyleXml = reader.readLfx2PsdSection(io);
}
else if (key == "Patt" || key == "Pat2" || key == "Pat3") {
KisAslReader reader;
QDomDocument pattern = reader.readPsdSectionPattern(io, blockSize);
embeddedPatterns << pattern;
}
else if (key == "Anno") {
}
else if (key == "clbl") {
}
else if (key == "infx") {
}
else if (key == "knko") {
}
else if (key == "spf") {
}
else if (key == "lclr") {
}
else if (key == "fxrp") {
}
else if (key == "grdm") {
}
else if (key == "lsct") {
quint32 dividerType = GARBAGE_VALUE_MARK;
SAFE_READ_EX(io, dividerType);
this->sectionDividerType = (psd_section_type)dividerType;
dbgFile << "Reading \"lsct\" block:";
dbgFile << ppVar(blockSize);
dbgFile << ppVar(dividerType);
if (blockSize >= 12) {
quint32 lsctSignature = GARBAGE_VALUE_MARK;
const quint32 refSignature1 = 0x3842494D; // '8BIM' in little-endian
SAFE_READ_SIGNATURE_EX(io, lsctSignature, refSignature1);
this->sectionDividerBlendMode = readFixedString(io);
dbgFile << ppVar(this->sectionDividerBlendMode);
}
// Animation
if (blockSize >= 14) {
/**
* "I don't care
* I don't care, no... !" (c)
*/
}
}
else if (key == "brst") {
}
else if (key == "SoCo") {
}
else if (key == "PtFl") {
}
else if (key == "GdFl") {
}
else if (key == "vmsk" || key == "vsms") { // If key is "vsms" then we are writing for (Photoshop CS6) and the document will have a "vscg" key
}
else if (key == "TySh") {
}
else if (key == "ffxi") {
}
else if (key == "lnsr") {
}
else if (key == "shpa") {
}
else if (key == "shmd") {
}
else if (key == "lyvr") {
}
else if (key == "tsly") {
}
else if (key == "lmgm") {
}
else if (key == "vmgm") {
}
else if (key == "plLd") { // Replaced by SoLd in CS3
}
else if (key == "linkD" || key == "lnk2" || key == "lnk3") {
}
else if (key == "phfl") {
}
else if (key == "blwh") {
}
else if (key == "CgEd") {
}
else if (key == "Txt2") {
}
else if (key == "vibA") {
}
else if (key == "pths") {
}
else if (key == "anFX") {
}
else if (key == "FMsk") {
}
else if (key == "SoLd") {
}
else if (key == "vstk") {
}
else if (key == "vsCg") {
}
else if (key == "sn2P") {
}
else if (key == "vogk") {
}
else if (key == "Mtrn" || key == "Mt16" || key == "Mt32") { // There is no data associated with these keys.
}
else if (key == "LMsk") {
}
else if (key == "expA") {
}
else if (key == "FXid") {
}
else if (key == "FEid") {
}
}
}
bool PsdAdditionalLayerInfoBlock::write(QIODevice */*io*/, KisNodeSP /*node*/)
{
return true;
}
bool PsdAdditionalLayerInfoBlock::valid()
{
return true;
}
void PsdAdditionalLayerInfoBlock::writeLuniBlockEx(QIODevice* io, const QString &layerName)
{
KisAslWriterUtils::writeFixedString("8BIM", io);
KisAslWriterUtils::writeFixedString("luni", io);
KisAslWriterUtils::OffsetStreamPusher<quint32> layerNameSizeTag(io, 2);
KisAslWriterUtils::writeUnicodeString(layerName, io);
}
void PsdAdditionalLayerInfoBlock::writeLsctBlockEx(QIODevice* io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
{
KisAslWriterUtils::writeFixedString("8BIM", io);
KisAslWriterUtils::writeFixedString("lsct", io);
KisAslWriterUtils::OffsetStreamPusher<quint32> sectionTypeSizeTag(io, 2);
SAFE_WRITE_EX(io, (quint32)sectionType);
QString realBlendModeKey = isPassThrough ? QString("pass") : blendModeKey;
KisAslWriterUtils::writeFixedString("8BIM", io);
KisAslWriterUtils::writeFixedString(realBlendModeKey, io);
}
void PsdAdditionalLayerInfoBlock::writeLfx2BlockEx(QIODevice* io, const QDomDocument &stylesXmlDoc)
{
KisAslWriterUtils::writeFixedString("8BIM", io);
KisAslWriterUtils::writeFixedString("lfx2", io);
KisAslWriterUtils::OffsetStreamPusher<quint32> lfx2SizeTag(io, 2);
try {
KisAslWriter writer;
writer.writePsdLfx2SectionEx(io, stylesXmlDoc);
} catch (KisAslWriterUtils::ASLWriteException &e) {
- qWarning() << "WARNING: Couldn't save layer style lfx2 block:" << PREPEND_METHOD(e.what());
+ warnKrita << "WARNING: Couldn't save layer style lfx2 block:" << PREPEND_METHOD(e.what());
// TODO: make this error recoverable!
throw e;
}
}
void PsdAdditionalLayerInfoBlock::writePattBlockEx(QIODevice* io, const QDomDocument &patternsXmlDoc)
{
KisAslWriterUtils::writeFixedString("8BIM", io);
KisAslWriterUtils::writeFixedString("Patt", io);
KisAslWriterUtils::OffsetStreamPusher<quint32> pattSizeTag(io, 2);
try {
KisAslPatternsWriter writer(patternsXmlDoc, io);
writer.writePatterns();
} catch (KisAslWriterUtils::ASLWriteException &e) {
- qWarning() << "WARNING: Couldn't save layer style patterns block:" << PREPEND_METHOD(e.what());
+ warnKrita << "WARNING: Couldn't save layer style patterns block:" << PREPEND_METHOD(e.what());
// TODO: make this error recoverable!
throw e;
}
}
diff --git a/krita/plugins/formats/psd/psd_header.h b/krita/plugins/formats/psd/psd_header.h
index 2de26a91c9c..1e5e64ec018 100644
--- a/krita/plugins/formats/psd/psd_header.h
+++ b/krita/plugins/formats/psd/psd_header.h
@@ -1,67 +1,67 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PSD_HEADER_H
#define PSD_HEADER_H
-#include <QDebug>
+#include <kis_debug.h>
#include <QtGlobal>
#include "psd.h"
class QIODevice;
class PSDHeader
{
public:
PSDHeader();
/**
* Reads a psd header from the given device.
*
* @return false if:
* <li>reading failed
* <li>if the 8BPS signature is not found
* <li>if the version is not 1 or 2
*/
bool read(QIODevice* device);
/**
* write the header data to the given device
*
* @return false if writing failed or if this is not a valid header
*/
bool write(QIODevice* device);
bool valid();
QString signature; // 8PBS
quint16 version; // 1 or 2
quint16 nChannels; // 1 - 56
quint32 height; // 1-30,000 or 1 - 300,000
quint32 width; // 1-30,000 or 1 - 300,000
quint16 channelDepth; // 1, 8, 16. XXX: check whether 32 is used!
psd_color_mode colormode;
QString error;
};
QDebug operator<<(QDebug dbg, const PSDHeader& header);
#endif // PSD_HEADER_H
diff --git a/krita/plugins/formats/psd/psd_image_data.cpp b/krita/plugins/formats/psd/psd_image_data.cpp
index 693f1433eb2..34df4f842e0 100644
--- a/krita/plugins/formats/psd/psd_image_data.cpp
+++ b/krita/plugins/formats/psd/psd_image_data.cpp
@@ -1,189 +1,189 @@
/*
* Copyright (C) 2011 by Siddharth Sharma <siddharth.kde@gmail.com>
*
* 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, see <http://www.gnu.org/licenses/>
*/
#include <psd_image_data.h>
#include <netinet/in.h> // htonl
#include <QFile>
-#include <QDebug>
+#include <kis_debug.h>
#include <QVector>
#include <QByteArray>
#include <QBuffer>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceTraits.h>
#include "psd_utils.h"
#include "compression.h"
#include "kis_iterator_ng.h"
#include "kis_paint_device.h"
#include <asl/kis_asl_reader_utils.h>
#include "psd_pixel_utils.h"
PSDImageData::PSDImageData(PSDHeader *header)
{
m_header = header;
}
PSDImageData::~PSDImageData() {
}
bool PSDImageData::read(QIODevice *io, KisPaintDeviceSP dev ) {
psdread(io, &m_compression);
quint64 start = io->pos();
m_channelSize = m_header->channelDepth/8;
m_channelDataLength = m_header->height * m_header->width * m_channelSize;
dbgFile << "Reading Image Data Block: compression" << m_compression << "channelsize" << m_channelSize << "number of channels" << m_header->nChannels;
switch (m_compression) {
case 0: // Uncompressed
for (int channel = 0; channel < m_header->nChannels; channel++) {
m_channelOffsets << 0;
ChannelInfo channelInfo;
channelInfo.channelId = channel;
channelInfo.compressionType = Compression::Uncompressed;
channelInfo.channelDataStart = start;
channelInfo.channelDataLength = m_header->width * m_header->height * m_channelSize;
start += channelInfo.channelDataLength;
m_channelInfoRecords.append(channelInfo);
}
break;
case 1: // RLE
{
quint32 rlelength = 0;
// The start of the actual channel data is _after_ the RLE rowlengths block
if (m_header->version == 1) {
start += m_header->nChannels * m_header->height * 2;
}
else if (m_header->version == 2) {
start += m_header->nChannels * m_header->height * 4;
}
for (int channel = 0; channel < m_header->nChannels; channel++) {
m_channelOffsets << 0;
quint32 sumrlelength = 0;
ChannelInfo channelInfo;
channelInfo.channelId = channel;
channelInfo.channelDataStart = start;
channelInfo.compressionType = Compression::RLE;
for (quint32 row = 0; row < m_header->height; row++ ) {
if (m_header->version == 1) {
psdread(io,(quint16*)&rlelength);
}
else if (m_header->version == 2) {
psdread(io,&rlelength);
}
channelInfo.rleRowLengths.append(rlelength);
sumrlelength += rlelength;
}
channelInfo.channelDataLength = sumrlelength;
start += channelInfo.channelDataLength;
m_channelInfoRecords.append(channelInfo);
}
break;
}
case 2: // ZIP without prediction
case 3: // ZIP with prediction
default:
break;
}
if (!m_channelInfoRecords.isEmpty()) {
QVector<ChannelInfo*> infoRecords;
QVector<ChannelInfo>::iterator it = m_channelInfoRecords.begin();
QVector<ChannelInfo>::iterator end = m_channelInfoRecords.end();
for (; it != end; ++it) {
infoRecords << &(*it);
}
const QRect imageRect(0, 0, m_header->width, m_header->height);
try {
PsdPixelUtils::readChannels(io, dev,
m_header->colormode,
m_channelSize,
imageRect,
infoRecords);
} catch (KisAslReaderUtils::ASLParseException &e) {
dev->clear();
return true;
}
}
return true;
}
bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev, bool hasAlpha)
{
// XXX: make the compression settting configurable. For now, always use RLE.
psdwrite(io, (quint16)Compression::RLE);
// now write all the channels in display order
// fill in the channel chooser, in the display order, but store the pixel index as well.
QRect rc(0, 0, m_header->width, m_header->height);
const int channelSize = m_header->channelDepth / 8;
const psd_color_mode colorMode = m_header->colormode;
QVector<PsdPixelUtils::ChannelWritingInfo> writingInfoList;
bool writeAlpha = hasAlpha &&
dev->colorSpace()->channelCount() != dev->colorSpace()->colorChannelCount();
const int numChannels =
writeAlpha ?
dev->colorSpace()->channelCount() :
dev->colorSpace()->colorChannelCount();
for (int i = 0; i < numChannels; i++) {
const int rleOffset = io->pos();
int channelId = writeAlpha && i == numChannels - 1 ? -1 : i;
writingInfoList <<
PsdPixelUtils::ChannelWritingInfo(channelId, -1, rleOffset);
io->seek(io->pos() + rc.height() * sizeof(quint16));
}
PsdPixelUtils::writePixelDataCommon(io, dev, rc,
colorMode, channelSize,
false, false, writingInfoList);
return true;
}
diff --git a/krita/plugins/formats/psd/psd_layer_section.cpp b/krita/plugins/formats/psd/psd_layer_section.cpp
index 88bf1091de1..acba3414865 100644
--- a/krita/plugins/formats/psd/psd_layer_section.cpp
+++ b/krita/plugins/formats/psd/psd_layer_section.cpp
@@ -1,592 +1,592 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_layer_section.h"
#include <boost/bind.hpp>
#include <QIODevice>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <kis_debug.h>
#include <kis_node.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_effect_mask.h>
#include <kis_image.h>
#include "kis_dom_utils.h"
#include "psd_header.h"
#include "psd_utils.h"
#include "compression.h"
#include <asl/kis_offset_on_exit_verifier.h>
#include <asl/kis_asl_reader_utils.h>
#include <kis_asl_layer_style_serializer.h>
#include <asl/kis_asl_writer_utils.h>
PSDLayerMaskSection::PSDLayerMaskSection(const PSDHeader& header)
: globalInfoSection(header),
m_header(header)
{
hasTransparency = false;
layerMaskBlockSize = 0;
nLayers = 0;
}
PSDLayerMaskSection::~PSDLayerMaskSection()
{
qDeleteAll(layers);
}
bool PSDLayerMaskSection::read(QIODevice* io)
{
bool retval = true; // be optimistic! <:-)
try {
retval = readImpl(io);
} catch (KisAslReaderUtils::ASLParseException &e) {
- qWarning() << "WARNING: PSD (emb. pattern):" << e.what();
+ warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
retval = false;
}
return retval;
}
bool PSDLayerMaskSection::readLayerInfoImpl(QIODevice* io)
{
quint32 layerInfoSectionSize = 0;
SAFE_READ_EX(io, layerInfoSectionSize);
if (layerInfoSectionSize & 0x1) {
- qWarning() << "WARNING: layerInfoSectionSize is NOT even! Fixing...";
+ warnKrita << "WARNING: layerInfoSectionSize is NOT even! Fixing...";
layerInfoSectionSize++;
}
{
SETUP_OFFSET_VERIFIER(layerInfoSectionTag, io, layerInfoSectionSize, 0);
dbgFile << "Layer info block size" << layerInfoSectionSize;
if (layerInfoSectionSize > 0 ) {
if (!psdread(io, &nLayers) || nLayers == 0) {
error = QString("Could not read read number of layers or no layers in image. %1").arg(nLayers);
return false;
}
hasTransparency = nLayers < 0; // first alpha channel is the alpha channel of the projection.
nLayers = qAbs(nLayers);
dbgFile << "Number of layers:" << nLayers;
dbgFile << "Has separate projection transparency:" << hasTransparency;
for (int i = 0; i < nLayers; ++i) {
dbgFile << "Going to read layer" << i << "pos" << io->pos();
dbgFile << "== Enter PSDLayerRecord";
PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
if (!layerRecord->read(io)) {
error = QString("Could not load layer %1: %2").arg(i).arg(layerRecord->error);
return false;
}
dbgFile << "== Leave PSDLayerRecord";
dbgFile << "Finished reading layer" << i << layerRecord->layerName << "blending mode"
<< layerRecord->blendModeKey << io->pos()
<< "Number of channels:" << layerRecord->channelInfoRecords.size();
layers << layerRecord;
}
}
// get the positions for the channels belonging to each layer
for (int i = 0; i < nLayers; ++i) {
dbgFile << "Going to seek channel positions for layer" << i << "pos" << io->pos();
if (i > layers.size()) {
error = QString("Expected layer %1, but only have %2 layers").arg(i).arg(layers.size());
return false;
}
PSDLayerRecord *layerRecord = layers.at(i);
for (int j = 0; j < layerRecord->nChannels; ++j) {
// save the current location so we can jump beyond this block later on.
quint64 channelStartPos = io->pos();
dbgFile << "\tReading channel image data for channel" << j << "from pos" << io->pos();
KIS_ASSERT_RECOVER(j < layerRecord->channelInfoRecords.size()) { return false; }
ChannelInfo* channelInfo = layerRecord->channelInfoRecords.at(j);
quint16 compressionType;
if (!psdread(io, &compressionType)) {
error = "Could not read compression type for channel";
return false;
}
channelInfo->compressionType = (Compression::CompressionType)compressionType;
dbgFile << "\t\tChannel" << j << "has compression type" << compressionType;
QRect channelRect = layerRecord->channelRect(channelInfo);
// read the rle row lengths;
if (channelInfo->compressionType == Compression::RLE) {
for(qint64 row = 0; row < channelRect.height(); ++row) {
//dbgFile << "Reading the RLE bytecount position of row" << row << "at pos" << io->pos();
quint32 byteCount;
if (m_header.version == 1) {
quint16 _byteCount;
if (!psdread(io, &_byteCount)) {
error = QString("Could not read byteCount for rle-encoded channel");
return 0;
}
byteCount = _byteCount;
}
else {
if (!psdread(io, &byteCount)) {
error = QString("Could not read byteCount for rle-encoded channel");
return 0;
}
}
////dbgFile << "rle byte count" << byteCount;
channelInfo->rleRowLengths << byteCount;
}
}
// we're beyond all the length bytes, rle bytes and whatever, this is the
// location of the real pixel data
channelInfo->channelDataStart = io->pos();
dbgFile << "\t\tstart" << channelStartPos
<< "data start" << channelInfo->channelDataStart
<< "data length" << channelInfo->channelDataLength
<< "pos" << io->pos();
// make sure we are at the start of the next channel data block
io->seek(channelStartPos + channelInfo->channelDataLength);
// this is the length of the actual channel data bytes
channelInfo->channelDataLength = channelInfo->channelDataLength - (channelInfo->channelDataStart - channelStartPos);
dbgFile << "\t\tchannel record" << j << "for layer" << i << "with id" << channelInfo->channelId
<< "starting postion" << channelInfo->channelDataStart
<< "with length" << channelInfo->channelDataLength
<< "and has compression type" << channelInfo->compressionType;
}
}
}
return true;
}
bool PSDLayerMaskSection::readImpl(QIODevice* io)
{
dbgFile << "reading layer section. Pos:" << io->pos() << "bytes left:" << io->bytesAvailable();
layerMaskBlockSize = 0;
if (m_header.version == 1) {
quint32 _layerMaskBlockSize = 0;
if (!psdread(io, &_layerMaskBlockSize) || _layerMaskBlockSize > (quint64)io->bytesAvailable()) {
error = QString("Could not read layer + mask block size. Got %1. Bytes left %2")
.arg(_layerMaskBlockSize).arg(io->bytesAvailable());
return false;
}
layerMaskBlockSize = _layerMaskBlockSize;
}
else if (m_header.version == 2) {
if (!psdread(io, &layerMaskBlockSize) || layerMaskBlockSize > (quint64)io->bytesAvailable()) {
error = QString("Could not read layer + mask block size. Got %1. Bytes left %2")
.arg(layerMaskBlockSize).arg(io->bytesAvailable());
return false;
}
}
quint64 start = io->pos();
dbgFile << "layer + mask section size" << layerMaskBlockSize;
if (layerMaskBlockSize == 0) {
dbgFile << "No layer + mask info, so no layers, only a background layer";
return true;
}
KIS_ASSERT_RECOVER(m_header.version == 1) { return false; }
if (!readLayerInfoImpl(io)) {
return false;
}
quint32 globalMaskBlockLength;
if (!psdread(io, &globalMaskBlockLength)) {
error = "Could not read global mask info block";
return false;
}
if (globalMaskBlockLength > 0) {
if (!psdread(io, &globalLayerMaskInfo.overlayColorSpace)) {
error = "Could not read global mask info overlay colorspace";
return false;
}
for (int i = 0; i < 4; ++i) {
if (!psdread(io, &globalLayerMaskInfo.colorComponents[i])) {
error = QString("Could not read mask info visualizaion color component %1").arg(i);
return false;
}
}
if (!psdread(io, &globalLayerMaskInfo.opacity)) {
error = "Could not read global mask info visualization opacity";
return false;
}
if (!psdread(io, &globalLayerMaskInfo.kind)) {
error = "Could not read global mask info visualization type";
return false;
}
}
// global additional sections
/**
* Newer versions of PSD have layers info block wrapped into
* 'Lr16' or 'Lr32' additional section, while the main block is
* absent.
*
* Here we pass the callback which should be used when such
* additional section is recognized.
*/
globalInfoSection.setExtraLayerInfoBlockHandler(boost::bind(&PSDLayerMaskSection::readLayerInfoImpl, this, _1));
globalInfoSection.read(io);
/* put us after this section so reading the next section will work even if we mess up */
io->seek(start + layerMaskBlockSize);
return true;
}
struct FlattenedNode {
FlattenedNode() : type(RASTER_LAYER) {}
KisNodeSP node;
enum Type {
RASTER_LAYER,
FOLDER_OPEN,
FOLDER_CLOSED,
SECTION_DIVIDER
};
Type type;
};
void addBackgroundIfNeeded(KisNodeSP root, QList<FlattenedNode> &nodes)
{
KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(root.data());
if (!group) return;
KoColor projectionColor = group->defaultProjectionColor();
if (projectionColor.opacityU8() == OPACITY_TRANSPARENT_U8) return;
KisPaintLayerSP layer =
new KisPaintLayer(group->image(),
i18nc("Automatically created layer name when saving into PSD", "Background"),
OPACITY_OPAQUE_U8);
projectionColor.convertTo(layer->paintDevice()->colorSpace());
layer->paintDevice()->setDefaultPixel(projectionColor.data());
{
FlattenedNode item;
item.node = layer;
item.type = FlattenedNode::RASTER_LAYER;
nodes << item;
}
}
void flattenNodes(KisNodeSP node, QList<FlattenedNode> &nodes)
{
KisNodeSP child = node->firstChild();
while (child) {
bool isGroupLayer = child->inherits("KisGroupLayer");
bool isRasterLayer = child->inherits("KisPaintLayer") || child->inherits("KisShapeLayer");
if (isGroupLayer) {
{
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::SECTION_DIVIDER;
nodes << item;
}
flattenNodes(child, nodes);
{
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::FOLDER_OPEN;
nodes << item;
}
} else if (isRasterLayer) {
FlattenedNode item;
item.node = child;
item.type = FlattenedNode::RASTER_LAYER;
nodes << item;
}
child = child->nextSibling();
}
}
KisNodeSP findOnlyTransparencyMask(KisNodeSP node, FlattenedNode::Type type)
{
if (type != FlattenedNode::FOLDER_OPEN &&
type != FlattenedNode::FOLDER_CLOSED &&
type != FlattenedNode::RASTER_LAYER) {
return 0;
}
KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
QList<KisEffectMaskSP> masks = layer->effectMasks();
if (masks.size() != 1) return 0;
KisEffectMaskSP onlyMask = masks.first();
return onlyMask->inherits("KisTransparencyMask") ? onlyMask : 0;
}
QDomDocument fetchLayerStyleXmlData(KisNodeSP node)
{
const KisLayer *layer = qobject_cast<KisLayer*>(node.data());
KisPSDLayerStyleSP layerStyle = layer->layerStyle();
if (!layerStyle) return QDomDocument();
KisAslLayerStyleSerializer serializer;
serializer.setStyles(QVector<KisPSDLayerStyleSP>() << layerStyle);
return serializer.formPsdXmlDocument();
}
inline QDomNode findNodeByKey(const QString &key, QDomNode parent) {
return KisDomUtils::findElementByAttibute(parent, "node", "key", key);
}
void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
{
QDomNode srcPatternsNode = findNodeByKey("Patterns", src.documentElement());
QDomNode dstPatternsNode = findNodeByKey("Patterns", dst.documentElement());
if (srcPatternsNode.isNull()) return;
if (dstPatternsNode.isNull()) {
dst = src;
return;
}
KIS_ASSERT_RECOVER_RETURN(!srcPatternsNode.isNull());
KIS_ASSERT_RECOVER_RETURN(!dstPatternsNode.isNull());
QDomNode node = srcPatternsNode.firstChild();
while(!node.isNull()) {
QDomNode importedNode = dst.importNode(node, true);
KIS_ASSERT_RECOVER_RETURN(!importedNode.isNull());
dstPatternsNode.appendChild(importedNode);
node = node.nextSibling();
}
}
bool PSDLayerMaskSection::write(QIODevice* io, KisNodeSP rootLayer)
{
bool retval = true;
try {
writeImpl(io, rootLayer);
} catch (KisAslWriterUtils::ASLWriteException &e) {
error = PREPEND_METHOD(e.what());
retval = false;
}
return retval;
}
void PSDLayerMaskSection::writeImpl(QIODevice* io, KisNodeSP rootLayer)
{
dbgFile << "Writing layer layer section";
// Build the whole layer structure
QList<FlattenedNode> nodes;
addBackgroundIfNeeded(rootLayer, nodes);
flattenNodes(rootLayer, nodes);
if (nodes.isEmpty()) {
throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
}
{
KisAslWriterUtils::OffsetStreamPusher<quint32> layerAndMaskSectionSizeTag(io, 2);
QDomDocument mergedPatternsXmlDoc;
{
KisAslWriterUtils::OffsetStreamPusher<quint32> layerInfoSizeTag(io, 4);
{
// number of layers (negative, because krita always has alpha)
const qint16 layersSize = -nodes.size();
SAFE_WRITE_EX(io, layersSize);
dbgFile << "Number of layers" << layersSize << "at" << io->pos();
}
// Layer records section
foreach(const FlattenedNode &item, nodes) {
KisNodeSP node = item.node;
PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
layers.append(layerRecord);
KisNodeSP onlyTransparencyMask = findOnlyTransparencyMask(node, item.type);
const QRect maskRect = onlyTransparencyMask ? onlyTransparencyMask->paintDevice()->exactBounds() : QRect();
const bool nodeVisible = node->visible();
const KoColorSpace *colorSpace = node->colorSpace();
const quint8 nodeOpacity = node->opacity();
const quint8 nodeClipping = 0;
const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
const QString nodeCompositeOp = node->compositeOpId();
const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer*>(node.data());
const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergedPatternsXmlDoc = stylesXmlDoc;
} else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
}
bool nodeIrrelevant = false;
QString nodeName;
KisPaintDeviceSP layerContentDevice;
psd_section_type sectionType;
if (item.type == FlattenedNode::RASTER_LAYER) {
nodeIrrelevant = false;
nodeName = node->name();
layerContentDevice = onlyTransparencyMask ? node->original() : node->projection();
sectionType = psd_other;
} else {
nodeIrrelevant = true;
nodeName = item.type == FlattenedNode::SECTION_DIVIDER ?
QString("</Layer group>") :
node->name();
layerContentDevice = 0;
sectionType =
item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider :
item.type == FlattenedNode::FOLDER_OPEN ? psd_open_folder :
psd_closed_folder;
}
// === no access to node anymore
QRect layerRect;
if (layerContentDevice) {
QRect rc = layerContentDevice->extent();
rc = rc.normalized();
// keep to the max of photoshop's capabilities
if (rc.width() > 30000) rc.setWidth(30000);
if (rc.height() > 30000) rc.setHeight(30000);
layerRect = rc;
}
layerRecord->top = layerRect.y();
layerRecord->left = layerRect.x();
layerRecord->bottom = layerRect.y() + layerRect.height();
layerRecord->right = layerRect.x() + layerRect.width();
// colors + alpha channel
// note: transparency mask not included
layerRecord->nChannels = colorSpace->colorChannelCount() + 1;
ChannelInfo *info = new ChannelInfo;
info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
layerRecord->channelInfoRecords << info;
// the rest is in display order: rgb, cmyk, lab...
for (int i = 0; i < (int)colorSpace->colorChannelCount(); ++i) {
info = new ChannelInfo;
info->channelId = i; // 0 for red, 1 = green, etc
layerRecord->channelInfoRecords << info;
}
layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
layerRecord->isPassThrough = nodeIsPassThrough;
layerRecord->opacity = nodeOpacity;
layerRecord->clipping = nodeClipping;
layerRecord->transparencyProtected = alphaLocked;
layerRecord->visible = nodeVisible;
layerRecord->irrelevant = nodeIrrelevant;
layerRecord->layerName = nodeName;
layerRecord->write(io,
layerContentDevice,
onlyTransparencyMask,
maskRect,
sectionType,
stylesXmlDoc);
}
dbgFile << "start writing layer pixel data" << io->pos();
// Now save the pixel data
foreach(PSDLayerRecord *layerRecord, layers) {
layerRecord->writePixelData(io);
}
}
{
// write the global layer mask info -- which is empty
const quint32 globalMaskSize = 0;
SAFE_WRITE_EX(io, globalMaskSize);
}
{
PsdAdditionalLayerInfoBlock globalInfoSection(m_header);
globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
}
}
}
diff --git a/krita/plugins/formats/psd/psd_loader.cpp b/krita/plugins/formats/psd/psd_loader.cpp
index 2f731d68250..bd9d49c0ce1 100644
--- a/krita/plugins/formats/psd/psd_loader.cpp
+++ b/krita/plugins/formats/psd/psd_loader.cpp
@@ -1,342 +1,342 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_loader.h"
#include <QApplication>
#include <kurl.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <KoCompositeOp.h>
#include <KoUnit.h>
#include <kis_annotation.h>
#include <kis_types.h>
#include <kis_paint_layer.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_transparency_mask.h>
#include <kis_asl_layer_style_serializer.h>
#include <kis_psd_layer_style_resource.h>
#include "kis_resource_server_provider.h"
#include "psd.h"
#include "psd_header.h"
#include "psd_colormode_block.h"
#include "psd_utils.h"
#include "psd_resource_section.h"
#include "psd_layer_section.h"
#include "psd_resource_block.h"
#include "psd_image_data.h"
PSDLoader::PSDLoader(KisDocument *doc)
: m_image(0)
, m_doc(doc)
, m_stop(false)
{
}
PSDLoader::~PSDLoader()
{
}
KisImageBuilder_Result PSDLoader::decode(const KUrl& uri)
{
// open the file
QFile f(uri.toLocalFile());
if (!f.exists()) {
return KisImageBuilder_RESULT_NOT_EXIST;
}
if (!f.open(QIODevice::ReadOnly)) {
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << "pos:" << f.pos();
PSDHeader header;
if (!header.read(&f)) {
dbgFile << "failed reading header: " << header.error;
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << header;
dbgFile << "Read header. pos:" << f.pos();
PSDColorModeBlock colorModeBlock(header.colormode);
if (!colorModeBlock.read(&f)) {
dbgFile << "failed reading colormode block: " << colorModeBlock.error;
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << "Read color mode block. pos:" << f.pos();
PSDImageResourceSection resourceSection;
if (!resourceSection.read(&f)) {
dbgFile << "failed image reading resource section: " << resourceSection.error;
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << "Read image resource section. pos:" << f.pos();
PSDLayerMaskSection layerSection(header);
if (!layerSection.read(&f)) {
dbgFile << "failed reading layer/mask section: " << layerSection.error;
return KisImageBuilder_RESULT_FAILURE;
}
dbgFile << "Read layer/mask section. " << layerSection.nLayers << "layers. pos:" << f.pos();
// Done reading, except possibly for the image data block, which is only relevant if there
// are no layers.
// Get the right colorspace
QPair<QString, QString> colorSpaceId = psd_colormode_to_colormodelid(header.colormode,
header.channelDepth);
if (colorSpaceId.first.isNull()) {
dbgFile << "Unsupported colorspace" << header.colormode << header.channelDepth;
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
// Get the icc profile from the image resource section
const KoColorProfile* profile = 0;
if (resourceSection.resources.contains(PSDImageResourceSection::ICC_PROFILE)) {
ICC_PROFILE_1039 *iccProfileData = dynamic_cast<ICC_PROFILE_1039*>(resourceSection.resources[PSDImageResourceSection::ICC_PROFILE]->resource);
if (iccProfileData ) {
profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first,
colorSpaceId.second,
iccProfileData->icc);
dbgFile << "Loaded ICC profile" << profile->name();
delete resourceSection.resources.take(PSDImageResourceSection::ICC_PROFILE);
}
}
// Create the colorspace
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile);
if (!cs) {
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
// Creating the KisImage
m_image = new KisImage(m_doc->createUndoStore(), header.width, header.height, cs, f.fileName());
Q_CHECK_PTR(m_image);
m_image->lock();
// set the correct resolution
if (resourceSection.resources.contains(PSDImageResourceSection::RESN_INFO)) {
RESN_INFO_1005 *resInfo = dynamic_cast<RESN_INFO_1005*>(resourceSection.resources[PSDImageResourceSection::RESN_INFO]->resource);
if (resInfo) {
m_image->setResolution(POINT_TO_INCH(resInfo->hRes), POINT_TO_INCH(resInfo->vRes));
// let's skip the unit for now; we can only set that on the KisDocument, and krita doesn't use it.
delete resourceSection.resources.take(PSDImageResourceSection::RESN_INFO);
}
}
// Preserve all the annotations
foreach(PSDResourceBlock *resourceBlock, resourceSection.resources.values()) {
m_image->addAnnotation(resourceBlock);
}
// Preserve the duotone colormode block for saving back to psd
if (header.colormode == DuoTone) {
KisAnnotationSP annotation = new KisAnnotation("DuotoneColormodeBlock",
i18n("Duotone Colormode Block"),
colorModeBlock.data);
m_image->addAnnotation(annotation);
}
// Read the projection into our single layer. Since we only read the projection when
// we have just one layer, we don't need to later on apply the alpha channel of the
// first layer to the projection if the number of layers is negative/
// See http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_16000.
if (layerSection.nLayers == 0) {
dbgFile << "Position" << f.pos() << "Going to read the projection into the first layer, which Photoshop calls 'Background'";
KisPaintLayerSP layer = new KisPaintLayer(m_image, i18n("Background"), OPACITY_OPAQUE_U8);
PSDImageData imageData(&header);
imageData.read(&f, layer->paintDevice());
m_image->addNode(layer, m_image->rootLayer());
// Only one layer, the background layer, so we're done.
m_image->unlock();
return KisImageBuilder_RESULT_OK;
}
// More than one layer, so now construct the Krita image from the info we read.
QStack<KisGroupLayerSP> groupStack;
groupStack.push(m_image->rootLayer());
typedef QPair<QDomDocument, KisLayerSP> LayerStyleMapping;
QVector<LayerStyleMapping> allStylesXml;
// read the channels for the various layers
for(int i = 0; i < layerSection.nLayers; ++i) {
PSDLayerRecord* layerRecord = layerSection.layers.at(i);
dbgFile << "Going to read channels for layer" << i << layerRecord->layerName;
KisLayerSP newLayer;
if (layerRecord->infoBlocks.keys.contains("lsct") &&
layerRecord->infoBlocks.sectionDividerType != psd_other) {
if (layerRecord->infoBlocks.sectionDividerType == psd_bounding_divider && !groupStack.isEmpty()) {
KisGroupLayerSP groupLayer = new KisGroupLayer(m_image, "temp", OPACITY_OPAQUE_U8);
m_image->addNode(groupLayer, groupStack.top());
groupStack.push(groupLayer);
newLayer = groupLayer;
}
else if ((layerRecord->infoBlocks.sectionDividerType == psd_open_folder || layerRecord->infoBlocks.sectionDividerType == psd_closed_folder) && !groupStack.isEmpty()) {
KisGroupLayerSP groupLayer = groupStack.pop();
groupLayer->setName(layerRecord->layerName);
groupLayer->setVisible(layerRecord->visible);
QString compositeOp = psd_blendmode_to_composite_op(layerRecord->infoBlocks.sectionDividerBlendMode);
// Krita doesn't support pass-through blend
// mode. Instead it is just a property of a goupr
// layer, so flip it
if (compositeOp == COMPOSITE_PASS_THROUGH) {
compositeOp = COMPOSITE_OVER;
groupLayer->setPassThroughMode(true);
}
groupLayer->setCompositeOp(compositeOp);
newLayer = groupLayer;
}
}
else {
KisPaintLayerSP layer = new KisPaintLayer(m_image, layerRecord->layerName, layerRecord->opacity);
layer->setCompositeOp(psd_blendmode_to_composite_op(layerRecord->blendModeKey));
const QDomDocument &styleXml = layerRecord->infoBlocks.layerStyleXml;
if (!styleXml.isNull()) {
allStylesXml << LayerStyleMapping(styleXml, layer);
}
if (!layerRecord->readPixelData(&f, layer->paintDevice())) {
dbgFile << "failed reading channels for layer: " << layerRecord->layerName << layerRecord->error;
return KisImageBuilder_RESULT_FAILURE;
}
if (!groupStack.isEmpty()) {
m_image->addNode(layer, groupStack.top());
}
else {
m_image->addNode(layer, m_image->root());
}
layer->setVisible(layerRecord->visible);
newLayer = layer;
}
foreach(ChannelInfo *channelInfo, layerRecord->channelInfoRecords) {
if (channelInfo->channelId < -1) {
KisTransparencyMaskSP mask = new KisTransparencyMask();
mask->setName(i18n("Transparency Mask"));
mask->initSelection(newLayer);
if (!layerRecord->readMask(&f, mask->paintDevice(), channelInfo)) {
dbgFile << "failed reading masks for layer: " << layerRecord->layerName << layerRecord->error;
}
m_image->addNode(mask, newLayer);
}
}
}
const QVector<QDomDocument> &embeddedPatterns =
layerSection.globalInfoSection.embeddedPatterns;
KisAslLayerStyleSerializer serializer;
if (!embeddedPatterns.isEmpty()) {
foreach (const QDomDocument &doc, embeddedPatterns) {
serializer.registerPSDPattern(doc);
}
}
QVector<KisPSDLayerStyleSP> allStylesForServer;
if (!allStylesXml.isEmpty()) {
foreach (const LayerStyleMapping &mapping, allStylesXml) {
serializer.readFromPSDXML(mapping.first);
if (serializer.styles().size() == 1) {
KisPSDLayerStyleSP layerStyle = serializer.styles().first();
KisLayerSP layer = mapping.second;
layerStyle->setName(layer->name());
allStylesForServer << layerStyle;
layer->setLayerStyle(layerStyle->clone());
} else {
- qWarning() << "WARNING: Couldn't read layer style!" << ppVar(serializer.styles());
+ warnKrita << "WARNING: Couldn't read layer style!" << ppVar(serializer.styles());
}
}
}
if (!allStylesForServer.isEmpty()) {
KisPSDLayerStyleCollectionResource *collection =
new KisPSDLayerStyleCollectionResource("Embedded PSD Styles.asl");
collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_image->objectName()));
KIS_ASSERT_RECOVER_NOOP(!collection->valid());
collection->setLayerStyles(allStylesForServer);
KIS_ASSERT_RECOVER_NOOP(collection->valid());
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
server->addResource(collection, false);
}
m_image->unlock();
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result PSDLoader::buildImage(const KUrl& uri)
{
if (uri.isEmpty())
return KisImageBuilder_RESULT_NO_URI;
if (!uri.isLocalFile()) {
return KisImageBuilder_RESULT_NOT_EXIST;
}
return decode(uri);
}
KisImageWSP PSDLoader::image()
{
return m_image;
}
void PSDLoader::cancel()
{
m_stop = true;
}
diff --git a/krita/plugins/formats/psd/psd_resource_block.h b/krita/plugins/formats/psd/psd_resource_block.h
index ca2d05801b1..bf662b250da 100644
--- a/krita/plugins/formats/psd/psd_resource_block.h
+++ b/krita/plugins/formats/psd/psd_resource_block.h
@@ -1,749 +1,749 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PSD_RESOURCE_BLOCK_H
#define PSD_RESOURCE_BLOCK_H
class QIODevice;
#include <klocale.h>
-#include <QDebug>
+#include <kis_debug.h>
#include <QString>
#include <QBuffer>
#include <kis_annotation.h>
#include <kis_debug.h>
#include "psd.h"
#include "psd_utils.h"
#include "psd_resource_section.h"
/**
* @brief The PSDResourceInterpreter struct interprets the data in a psd resource block
*/
class PSDInterpretedResource
{
public:
virtual ~PSDInterpretedResource() {}
virtual bool interpretBlock(QByteArray /*data*/) { return true; }
virtual bool createBlock(QByteArray & /*data*/) { return true; }
virtual bool valid() { return true; }
virtual QString displayText() { return QString(); }
QString error;
protected:
void startBlock(QBuffer &buf, PSDImageResourceSection::PSDResourceID id, quint32 size) {
if (!buf.isOpen()) {
buf.open(QBuffer::WriteOnly);
}
buf.write("8BIM", 4);
psdwrite(&buf, (quint16)id);
psdwrite(&buf, (quint16)0); // We simply never save out the name, for now
psdwrite(&buf, (quint32)size);
}
};
/**
* Contains the unparsed contents of the image resource blocks
*/
class PSDResourceBlock : public KisAnnotation
{
public:
PSDResourceBlock();
~PSDResourceBlock()
{
delete resource;
}
QString displayText() const {
if (resource) {
return resource->displayText();
}
return i18n("Unparsed Resource Block");
}
bool read(QIODevice* io);
bool write(QIODevice* io);
bool valid();
quint16 identifier;
QString name;
quint32 dataSize;
QByteArray data;
PSDInterpretedResource *resource;
QString error;
};
/* 0x03e9 - Optional - Mac print manager print info record */
struct MAC_PRINT_INFO_1001 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading MAC_PRINT_INFO_1001";
return true;
}
};
/* 0x03ed - ResolutionInfo structure */
struct RESN_INFO_1005 : public PSDInterpretedResource
{
// XXX: Krita only uses INCH internally
enum PSDUnit {
PSD_UNIT_INCH = 1, /* inches */
PSD_UNIT_CM = 2, /* cm */
PSD_UNIT_POINT = 3, /* points (72 points = 1 inch) */
PSD_UNIT_PICA = 4, /* pica ( 6 pica = 1 inch) */
PSD_UNIT_COLUMN = 5 /* columns ( column defined in ps prefs, default = 2.5 inches) */
};
RESN_INFO_1005()
: hRes(300)
, hResUnit(PSD_UNIT_INCH)
, widthUnit(PSD_UNIT_INCH)
, vRes(300)
, vResUnit(PSD_UNIT_INCH)
, heightUnit(PSD_UNIT_INCH)
{}
virtual bool interpretBlock(QByteArray data);
virtual bool createBlock(QByteArray & data);
Fixed hRes;
quint16 hResUnit;
quint16 widthUnit;
Fixed vRes;
quint16 vResUnit;
quint16 heightUnit;
};
/* 0x03ee - Alpha channel names */
struct ALPHA_NAMES_1006 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading ALPHA_NAMES_1006";
return true;
}
};
/* 0x03ef - DisplayInfo structure */
struct DISPLAY_INFO_1007 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading DISPLAY_INFO_1007";
return true;
}
};
/* 0x03f0 - Optional - Caption string */
struct CAPTION_1008 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading CAPTION_1008";
return true;
}
};
/* 0x03f1 - Border info */
struct BORDER_INFO_1009 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading BORDER_INFO_1009";
return true;
}
};
/* 0x03f2 - Background colour */
struct BACKGROUND_COL_1010 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading BACKGROUND_COL_1010";
return true;
}
};
/* 0x03f3 - Print flags */
struct PRINT_FLAGS_1011 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading PRINT_FLAGS_1011";
return true;
}
};
/* 0x03f4 - Greyscale and multichannel halftoning info */
struct GREY_HALFTONE_1012 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading GREY_HALFTONE_1012";
return true;
}
};
/* 0x03f5 - Colour halftoning info */
struct COLOR_HALFTONE_1013 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading COLOR_HALFTONE_1013";
return true;
}
};
/* 0x03f6 - Duotone halftoning info */
struct DUOTONE_HALFTONE_1014 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading DUOTONE_HALFTONE_1014";
return true;
}
};
/* 0x03f7 - Greyscale and multichannel transfer functions */
struct GREY_XFER_1015 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading GREY_XFER_1015";
return true;
}
};
/* 0x03f8 - Colour transfer functions */
struct COLOR_XFER_1016 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading COLOR_XFER_1016";
return true;
}
};
/* 0x03f9 - Duotone transfer functions */
struct DUOTONE_XFER_1017 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading DUOTONE_XFER_1017";
return true;
}
};
/* 0x03fa - Duotone image information */
struct DUOTONE_INFO_1018 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading DUOTONE_INFO_1018";
return true;
}
};
/* 0x03fb - Effective black & white values for dot range */
struct EFFECTIVE_BW_1019 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading EFFECTIVE_BW_1019";
return true;
}
};
/* 0x03fd - EPS options */
struct EPS_OPT_1021 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading EPS_OPT_1021";
return true;
}
};
/* 0x03fe - Quick mask info */
struct QUICK_MASK_1022 : public PSDInterpretedResource
{ bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading QUICK_MASK_1022";
return true;
}
};
/* 0x0400 - Layer state info */
struct LAYER_STATE_1024 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading LAYER_STATE_1024";
return true;
}
};
/* 0x0401 - Working path (not saved) */
struct WORKING_PATH_1025 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading WORKING_PATH_1025";
return true;
}
};
/* 0x0402 - Layers group info */
struct LAYER_GROUP_1026 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading LAYER_GROUP_1026";
return true;
}
};
/* 0x0404 - IPTC-NAA record (IMV4.pdf) */
struct IPTC_NAA_DATA_1028 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading IPTC_NAA_DATA_1028";
return true;
}
};
/* 0x0405 - Image mode for raw format files */
struct IMAGE_MODE_RAW_1029 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading IMAGE_MODE_RAW_1029";
return true;
}
};
/* 0x0406 - JPEG quality */
struct JPEG_QUAL_1030 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading JPEG_QUAL_1030";
return true;
}
};
/* 0x0408 - Grid & guide info */
struct GRID_GUIDE_1032 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading GRID_GUIDE_1032";
return true;
}
};
/* 0x0409 - Thumbnail resource */
struct THUMB_RES_1033 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading THUMB_RES_1033";
return true;
}
};
/* 0x040a - Copyright flag */
struct COPYRIGHT_FLG_1034 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading COPYRIGHT_FLG_1034";
return true;
}
};
/* 0x040b - URL string */
struct URL_1035 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading URL_1035";
return true;
}
};
/* 0x040c - Thumbnail resource */
struct THUMB_RES2_1036 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading THUMB_RES2_1036";
return true;
}
};
/* 0x040d - Global angle */
struct GLOBAL_ANGLE_1037 : public PSDInterpretedResource
{
GLOBAL_ANGLE_1037()
: angle(30)
{}
bool interpretBlock(QByteArray data)
{
dbgFile << "Reading GLOBAL_ANGLE_1037";
QDataStream ds(data);
ds.setByteOrder(QDataStream::BigEndian);
ds >> angle;
return true;
}
virtual bool createBlock(QByteArray & data)
{
QBuffer buf(&data);
startBlock(buf, PSDImageResourceSection::GLOBAL_ANGLE, 4);
psdwrite(&buf, (quint32)angle);
return true;
}
virtual bool valid() { return true; }
virtual QString displayText() { return QString("Global Angle: %1").arg(angle); }
qint32 angle;
};
/* 0x040e - Colour samplers resource */
struct COLOR_SAMPLER_1038 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading COLOR_SAMPLER_1038";
return true;
}
};
/* 0x040f - ICC Profile */
struct ICC_PROFILE_1039 : public PSDInterpretedResource
{
virtual bool interpretBlock(QByteArray data);
virtual bool createBlock(QByteArray & data);
QByteArray icc;
};
/* 0x0410 - Watermark */
struct WATERMARK_1040 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading WATERMARK_1040";
return true;
}
};
/* 0x0411 - Do not use ICC profile flag */
struct ICC_UNTAGGED_1041 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading ICC_UNTAGGED_1041";
return true;
}
};
/* 0x0412 - Show / hide all effects layers */
struct EFFECTS_VISIBLE_1042 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading EFFECTS_VISIBLE_1042";
return true;
}
};
/* 0x0413 - Spot halftone */
struct SPOT_HALFTONE_1043 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading SPOT_HALFTONE_1043";
return true;
}
};
/* 0x0414 - Document specific IDs */
struct DOC_IDS_1044 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading DOC_IDS_1044";
return true;
}
};
/* 0x0415 - Unicode alpha names */
struct ALPHA_NAMES_UNI_1045 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading ALPHA_NAMES_UNI_1045";
return true;
}
};
/* 0x0416 - Indexed colour table count */
struct IDX_COL_TAB_CNT_1046 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading IDX_COL_TAB_CNT_1046";
return true;
}
};
/* 0x0417 - Index of transparent colour (if any) */
struct IDX_TRANSPARENT_1047 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading IDX_TRANSPARENT_1047";
return true;
}
};
/* 0x0419 - Global altitude */
struct GLOBAL_ALT_1049 : public PSDInterpretedResource
{
GLOBAL_ALT_1049()
: altitude(30)
{}
bool interpretBlock(QByteArray data)
{
dbgFile << "Reading GLOBAL_ALT_1049";
QDataStream ds(data);
ds.setByteOrder(QDataStream::BigEndian);
ds >> altitude;
return true;
}
virtual bool createBlock(QByteArray & data)
{
QBuffer buf(&data);
startBlock(buf, PSDImageResourceSection::GLOBAL_ALT, 4);
psdwrite(&buf, (quint32)altitude);
return true;
}
virtual bool valid() { return true; }
virtual QString displayText() { return QString("Global Altitude: %1").arg(altitude); }
qint32 altitude;
};
/* 0x041a - Slices */
struct SLICES_1050 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading SLICES_1050";
return true;
}
};
/* 0x041b - Workflow URL - Unicode string */
struct WORKFLOW_URL_UNI_1051 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading WORKFLOW_URL_UNI_1051";
return true;
}
};
/* 0x041c - Jump to XPEP (?) */
struct JUMP_TO_XPEP_1052 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "JUMP_TO_XPEP_1052";
return true;
}
};
/* 0x041d - Alpha IDs */
struct ALPHA_ID_1053 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "ALPHA_ID_1053";
return true;
}
};
/* 0x041e - URL list - unicode */
struct URL_LIST_UNI_1054 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "URL_LIST_UNI_1054";
return true;
}
};
/* 0x0421 - Version info */
struct VERSION_INFO_1057 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "VERSION_INFO_1057";
return true;
}
};
/* 0x0422 - Exif data block */
struct EXIF_DATA_1058 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading EXIF_DATA_1058";
return true;
}
};
/* 0x0424 - XMP data block */
struct XMP_DATA_1060 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading XMP_DATA_1060";
return true;
}
};
/* 0x07d0 - First path info block */
struct PATH_INFO_FIRST_2000 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "PATH_INFO_FIRST_2000";
return true;
}
};
/* 0x0bb6 - Last path info block */
struct PATH_INFO_LAST_2998 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "PATH_INFO_LAST_2998";
return true;
}
};
/* 0x0bb7 - Name of clipping path */
struct CLIPPING_PATH_2999 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading CLIPPING_PATH_2999";
return true;
}
};
/* 0x2710 - Print flags */
struct PRINT_FLAGS_2_10000 : public PSDInterpretedResource
{
bool interpretBlock(QByteArray /*data*/)
{
dbgFile << "Reading PRINT_FLAGS_2_10000";
return true;
}
};
#endif // PSD_RESOURCE_BLOCK_H
diff --git a/krita/plugins/formats/psd/psd_resource_section.cpp b/krita/plugins/formats/psd/psd_resource_section.cpp
index fb0dda7d9d4..43bf46e1dc5 100644
--- a/krita/plugins/formats/psd/psd_resource_section.cpp
+++ b/krita/plugins/formats/psd/psd_resource_section.cpp
@@ -1,230 +1,230 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_resource_section.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QIODevice>
#include <QBuffer>
#include <kis_debug.h>
#include "psd_utils.h"
#include "psd_resource_block.h"
PSDImageResourceSection::PSDImageResourceSection()
{
}
PSDImageResourceSection::~PSDImageResourceSection()
{
resources.clear();
}
bool PSDImageResourceSection::read(QIODevice* io)
{
quint32 resourceSectionLength = 0;
if (!psdread(io, &resourceSectionLength)) {
error = "Could not read image resource section length";
return false;
}
dbgFile << "Image Resource Sectionlength:" << resourceSectionLength << ", starts at:" << io->pos();
QByteArray ba = io->read(resourceSectionLength);
if ((quint32)ba.size() != resourceSectionLength) {
error = "Could not read all resources";
return false;
}
QBuffer buf;
buf.setBuffer(&ba);
buf.open(QBuffer::ReadOnly);
while (!buf.atEnd()) {
PSDResourceBlock* block = new PSDResourceBlock();
if (!block->read(&buf)) {
error = "Error reading block: " + block->error;
dbgFile << error << ", skipping.";
continue;
}
dbgFile << "resource block created. Type:" << block->identifier
<< "name" << block->name
<< "size" << block->dataSize
<< "," << buf.bytesAvailable() << "bytes to go";
resources[(PSDResourceID)block->identifier] = block;
}
dbgFile << "Read" << resources.size() << "Image Resource Blocks";
return valid();
}
bool PSDImageResourceSection::write(QIODevice* io)
{
Q_UNUSED(io);
if (!valid()) {
error = "Resource Section is Invalid";
return false;
}
// First write all the sections
QByteArray ba;
QBuffer buf;
buf.setBuffer(&ba);
buf.open(QBuffer::WriteOnly);
foreach(PSDResourceBlock* block, resources) {
if (!block->write(&buf)) {
error = block->error;
return false;
}
}
buf.close();
// Then get the size
quint32 resourceBlockLength = ba.size();
dbgFile << "resource section has size" << resourceBlockLength;
psdwrite(io, resourceBlockLength);
// and write the whole buffer;
return (io->write(ba.constData(), ba.size()) == resourceBlockLength);
}
bool PSDImageResourceSection::valid()
{
return true;
}
QString PSDImageResourceSection::idToString(PSDImageResourceSection::PSDResourceID id)
{
switch(id) {
case UNKNOWN: return "Unknown";
case PS2_IMAGE_INFO : return "0x03e8 - Obsolete - ps 2.0 image info";
case MAC_PRINT_INFO : return "0x03e9 - Optional - Mac print manager print info record";
case PS2_COLOR_TAB : return "0x03eb - Obsolete - ps 2.0 indexed colour table";
case RESN_INFO : return "0x03ed - ResolutionInfo structure";
case ALPHA_NAMES : return "0x03ee - Alpha channel names";
case DISPLAY_INFO : return "0x03ef - DisplayInfo structure";
case CAPTION : return "0x03f0 - Optional - Caption string";
case BORDER_INFO : return "0x03f1 - Border info";
case BACKGROUND_COL : return "0x03f2 - Background colour";
case PRINT_FLAGS : return "0x03f3 - Print flags";
case GREY_HALFTONE : return "0x03f4 - Greyscale and multichannel halftoning info";
case COLOR_HALFTONE : return "0x03f5 - Colour halftoning info";
case DUOTONE_HALFTONE : return "0x03f6 - Duotone halftoning info";
case GREY_XFER : return "0x03f7 - Greyscale and multichannel transfer functions";
case COLOR_XFER : return "0x03f8 - Colour transfer functions";
case DUOTONE_XFER : return "0x03f9 - Duotone transfer functions";
case DUOTONE_INFO : return "0x03fa - Duotone image information";
case EFFECTIVE_BW : return "0x03fb - Effective black & white values for dot range";
case OBSOLETE_01 : return "0x03fc - Obsolete";
case EPS_OPT : return "0x03fd - EPS options";
case QUICK_MASK : return "0x03fe - Quick mask info";
case OBSOLETE_02 : return "0x03ff - Obsolete";
case LAYER_STATE : return "0x0400 - Layer state info";
case WORKING_PATH : return "0x0401 - Working path (not saved)";
case LAYER_GROUP : return "0x0402 - Layers group info";
case OBSOLETE_03 : return "0x0403 - Obsolete";
case IPTC_NAA_DATA : return "0x0404 - IPTC-NAA record (IMV4.pdf)";
case IMAGE_MODE_RAW : return "0x0405 - Image mode for raw format files";
case JPEG_QUAL : return "0x0406 - JPEG quality";
case GRID_GUIDE : return "0x0408 - Grid & guide info";
case THUMB_RES : return "0x0409 - Thumbnail resource";
case COPYRIGHT_FLG : return "0x040a - Copyright flag";
case URL : return "0x040b - URL string";
case THUMB_RES2 : return "0x040c - Thumbnail resource";
case GLOBAL_ANGLE : return "0x040d - Global angle";
case COLOR_SAMPLER : return "0x040e - Colour samplers resource";
case ICC_PROFILE : return "0x040f - ICC Profile";
case WATERMARK : return "0x0410 - Watermark";
case ICC_UNTAGGED : return "0x0411 - Do not use ICC profile flag";
case EFFECTS_VISIBLE : return "0x0412 - Show / hide all effects layers";
case SPOT_HALFTONE : return "0x0413 - Spot halftone";
case DOC_IDS : return "0x0414 - Document specific IDs";
case ALPHA_NAMES_UNI : return "0x0415 - Unicode alpha names";
case IDX_COL_TAB_CNT : return "0x0416 - Indexed colour table count";
case IDX_TRANSPARENT : return "0x0417 - Index of transparent colour (if any)";
case GLOBAL_ALT : return "0x0419 - Global altitude";
case SLICES : return "0x041a - Slices";
case WORKFLOW_URL_UNI : return "0x041b - Workflow URL - Unicode string";
case JUMP_TO_XPEP : return "0x041c - Jump to XPEP (?)";
case ALPHA_ID : return "0x041d - Alpha IDs";
case URL_LIST_UNI : return "0x041e - URL list - unicode";
case VERSION_INFO : return "0x0421 - Version info";
case EXIF_DATA : return "0x0422 - (Photoshop 7.0) EXIF data 1. See http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf";
case EXIF_DATA_3 : return "0x0423 - (Photoshop 7.0) EXIF data 3. See http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf";
case XMP_DATA : return "0x0424 - XMP data block";
case CAPTION_DIGEST : return "0x0425 - (Photoshop 7.0) Caption digest. 16 bytes: RSA Data Security, MD5 message-digest algorithm";
case PRINT_SCALE : return "0x0426 - (Photoshop 7.0) Print scale. 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). 4 bytes x location (floating point). 4 bytes y location (floating point). 4 bytes scale (floating point)";
case PIXEL_ASPECT_RATION : return "0x0428 - (Photoshop CS) Pixel Aspect Ratio. 4 bytes (version = 1 or 2), 8 bytes double, x / y of a pixel. Version 2, attempting to correct values for NTSC and PAL, previously off by a factor of approx. 5%.";
case LAYER_COMPS : return "0x0429 - (Photoshop CS) Layer Comps. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)";
case ALTERNATE_DUOTONE : return "0x042A - (Photoshop CS) Alternate Duotone Colors. 2 bytes (version = 1), 2 bytes count, following is repeated for each count: [ Color: 2 bytes for space followed by 4 * 2 byte color component ], following this is another 2 byte count, usually 256, followed by Lab colors one byte each for L, a, b. This resource is not read or used by Photoshop.";
case ALTERNATE_SPOT : return "0x042B - (Photoshop CS)Alternate Spot Colors. 2 bytes (version = 1), 2 bytes channel count, following is repeated for each count: 4 bytes channel ID, Color: 2 bytes for space followed by 4 * 2 byte color component. This resource is not read or used by Photoshop.";
case LAYER_SELECTION_ID : return "0x042D - (Photoshop CS2) Layer Selection ID(s). 2 bytes count, following is repeated for each count: 4 bytes layer ID";
case HDR_TONING : return "0x042E - (Photoshop CS2) HDR Toning information";
case CS2_PRINT_INFO : return "0x042F - (Photoshop CS2) Print info";
case LAYER_GROUP_ENABLED_ID: return "0x0430 - (Photoshop CS2) Layer Group(s) Enabled ID. 1 byte for each layer in the document, repeated by length of the resource. NOTE: Layer groups have start and end markers";
case COLOR_SAMPLERS : return "0x0431 - (Photoshop CS3) Color samplers resource. Also see ID 1038 for old format. See See Color samplers resource format.";
case MEASUREMENT_SCALE : return "0x0432 - (Photoshop CS3) Measurement Scale. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)";
case TIMELINE_INFO : return "0x0433 - (Photoshop CS3) Timeline Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)";
case SHEET_DISCLOSURE : return "0x0434 - (Photoshop CS3) Sheet Disclosure. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)";
case CS3_DISPLAY_INFO : return "0x0435 - (Photoshop CS3) DisplayInfo structure to support floating point clors. Also see ID 1007. See Appendix A in Photoshop API Guide.pdf .";
case ONION_SKINS : return "0x0436 - (Photoshop CS3) Onion Skins. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure)";
case COUNT_INFO : return "0x0438 - (Photoshop CS4) Count Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the count in the document. See the Count Tool.";
case CS5_PRINT_INFO : return "0x043A - (Photoshop CS5) Print Information. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current print settings in the document. The color management options.";
case CS5_PRINT_STYLE : return "0x043B - (Photoshop CS5) Print Style. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current print style in the document. The printing marks, labels, ornaments, etc.";
case CS5_NSPrintInfo : return "0x043C - (Photoshop CS5) Macintosh NSPrintInfo. Variable OS specific info for Macintosh. NSPrintInfo. It is recommened that you do not interpret or use this data.";
case CS5_WIN_DEVMODE : return "0x043D - (Photoshop CS5) Windows DEVMODE. Variable OS specific info for Windows. DEVMODE. It is recommened that you do not interpret or use this data.";
case CS6_AUTOSAVE_FILE_PATH : return "0x043E - (Photoshop CS6) Auto Save File Path. Unicode string. It is recommened that you do not interpret or use this data.";
case CS6_AUTOSAVE_FORMAT : return "0x043F - (Photoshop CS6) Auto Save Format. Unicode string. It is recommened that you do not interpret or use this data.";
case CC_PATH_SELECTION_SATE : return "0x0440 - (Photoshop CC) Path Selection State. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the current path selection state.";
case PATH_INFO_FIRST : return "0x07d0 - First path info block";
case PATH_INFO_LAST : return "0x0bb6 - Last path info block";
case CLIPPING_PATH : return "0x0bb7 - Name of clipping path";
case CC_ORIGIN_PATH_INFO : return "0x0BB8 (Photoshop CC) Origin Path Info. 4 bytes (descriptor version = 16), Descriptor (see See Descriptor structure) Information about the origin path data.";
case PLUGIN_RESOURCE_START : return "0x0FA0-0x1387 Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation ";
case PLUGIN_RESOURCE_END : return "Last plug-in resource";
case IMAGE_READY_VARS : return "0x1B58 Image Ready variables. XML representation of variables definition";
case IMAGE_READY_DATA_SETS : return "0x1B59 Image Ready data sets";
case LIGHTROOM_WORKFLOW : return "0x1F40 (Photoshop CS3) Lightroom workflow, if present the document is in the middle of a Lightroom workflow.";
case PRINT_FLAGS_2 : return "0x2710 - Print flags";
default: {
if (id > PATH_INFO_FIRST && id < PATH_INFO_LAST) return "Path Info Block";
if (id > PLUGIN_RESOURCE_START && id < PLUGIN_RESOURCE_END) return "Plug-In Resource";
}
};
return QString("Unknown Resource Block: %1").arg(id);
}
diff --git a/krita/plugins/formats/psd/tests/CMakeLists.txt b/krita/plugins/formats/psd/tests/CMakeLists.txt
index e8178dd809f..0c189c65d31 100644
--- a/krita/plugins/formats/psd/tests/CMakeLists.txt
+++ b/krita/plugins/formats/psd/tests/CMakeLists.txt
@@ -1,36 +1,36 @@
include_directories(${CMAKE_BINARY_DIR}/krita/libpsd) #For kispsd_include.h
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${CMAKE_SOURCE_DIR}/.. ${CMAKE_SOURCE_DIR}/krita/sdk/tests )
macro_add_unittest_definitions()
########### next target ###############
set(psd_header_test_SRCS psd_header_test.cpp ${CMAKE_SOURCE_DIR}/krita/libpsd/psd_utils.cpp ../psd_header.cpp )
kde4_add_unit_test(psd_header_test TESTNAME krita-psd-psd_header_test ${psd_header_test_SRCS})
-target_link_libraries(psd_header_test ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(psd_header_test kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(psd_utils_test_SRCS psd_utils_test.cpp ${CMAKE_SOURCE_DIR}/krita/libpsd/psd_utils.cpp)
kde4_add_unit_test(psd_utils_test TESTNAME krita-psd-psd_utils_test ${psd_utils_test_SRCS})
-target_link_libraries(psd_utils_test ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(psd_utils_test kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(psd_colormode_block_test_SRCS psd_colormode_block_test.cpp ../psd_colormode_block.cpp ../psd_header.cpp ${CMAKE_SOURCE_DIR}/krita/libpsd/psd_utils.cpp)
kde4_add_unit_test(psd_colormode_block_test TESTNAME krita-psd-psd_colormode_block_test ${psd_colormode_block_test_SRCS})
-target_link_libraries(psd_colormode_block_test ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(psd_colormode_block_test kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(compression_test_SRCS compression_test.cpp ${CMAKE_SOURCE_DIR}/krita/libpsd/compression.cpp)
kde4_add_unit_test(compression_test TESTNAME krita-psd-compression_test ${compression_test_SRCS})
-target_link_libraries(compression_test ${KDE4_KDEUI_LIBS} Qt5::Test)
+target_link_libraries(compression_test kritaglobal ${KDE4_KDEUI_LIBS} Qt5::Test)
########### next target ###############
set(kis_psd_test_SRCS kis_psd_test.cpp )
kde4_add_unit_test(kis_psd_test TESTNAME krita-plugins-formats-psd_test ${kis_psd_test_SRCS})
-target_link_libraries(kis_psd_test ${KDE4_KDEUI_LIBS} kritaui Qt5::Test)
+target_link_libraries(kis_psd_test kritaglobal ${KDE4_KDEUI_LIBS} kritaui Qt5::Test)
diff --git a/krita/plugins/formats/psd/tests/compression_test.cpp b/krita/plugins/formats/psd/tests/compression_test.cpp
index e68da10712c..62727252e00 100644
--- a/krita/plugins/formats/psd/tests/compression_test.cpp
+++ b/krita/plugins/formats/psd/tests/compression_test.cpp
@@ -1,100 +1,102 @@
/*
* Copyright (C) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "compression_test.h"
#include <QTest>
#include <QCoreApplication>
#include <klocale.h>
#include <qtest_kde.h>
#include "../../../../libpsd/compression.h"
#include <QByteArray>
#include <QBuffer>
#include <QDataStream>
#include <math.h>
+#include <kis_debug.h>
+
void CompressionTest::testCompressionRLE()
{
QByteArray ba("Twee eeee aaaaa asdasda47892347981 wwwwwwwwwwwwWWWWWWWWWW");
QByteArray compressed = Compression::compress(ba, Compression::RLE);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
QByteArray uncompressed = Compression::uncompress(ba.size(), compressed, Compression::RLE);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
ba.clear();
QDataStream ds(&ba, QIODevice::WriteOnly);
for (int i = 0; i < 500; ++i) {
ds << rand();
}
compressed = Compression::compress(ba, Compression::RLE);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
uncompressed = Compression::uncompress(ba.size(), compressed, Compression::RLE);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
}
void CompressionTest::testCompressionZIP()
{
QByteArray ba("Twee eeee aaaaa asdasda47892347981 wwwwwwwwwwwwWWWWWWWWWW");
QByteArray compressed = Compression::compress(ba, Compression::ZIP);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
QByteArray uncompressed = Compression::uncompress(ba.size(), compressed, Compression::ZIP);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
ba.clear();
QDataStream ds(&ba, QIODevice::WriteOnly);
for (int i = 0; i < 500; ++i) {
ds << rand();
}
compressed = Compression::compress(ba, Compression::ZIP);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
uncompressed = Compression::uncompress(ba.size(), compressed, Compression::ZIP);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
}
void CompressionTest::testCompressionUncompressed()
{
QByteArray ba("Twee eeee aaaaa asdasda47892347981 wwwwwwwwwwwwWWWWWWWWWW");
QByteArray compressed = Compression::compress(ba, Compression::Uncompressed);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
QByteArray uncompressed = Compression::uncompress(ba.size(), compressed, Compression::Uncompressed);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
ba.clear();
QDataStream ds(&ba, QIODevice::WriteOnly);
for (int i = 0; i < 500; ++i) {
ds << rand();
}
compressed = Compression::compress(ba, Compression::Uncompressed);
- qDebug() << compressed.size() << "uncompressed" << ba.size();
+ dbgKrita << compressed.size() << "uncompressed" << ba.size();
uncompressed = Compression::uncompress(ba.size(), compressed, Compression::Uncompressed);
QVERIFY(qstrcmp(ba, uncompressed) == 0);
}
QTEST_KDEMAIN(CompressionTest, GUI)
diff --git a/krita/plugins/formats/psd/tests/kis_psd_test.cpp b/krita/plugins/formats/psd/tests/kis_psd_test.cpp
index bf7d0255b36..5d4c37a3a2e 100644
--- a/krita/plugins/formats/psd/tests/kis_psd_test.cpp
+++ b/krita/plugins/formats/psd/tests/kis_psd_test.cpp
@@ -1,340 +1,340 @@
/*
* Copyright (C) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_psd_test.h"
#include <QTest>
#include <QCoreApplication>
#include <qtest_kde.h>
#include "filestest.h"
#ifndef FILES_DATA_DIR
#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita"
#endif
#include <KoPattern.h>
#include "kis_group_layer.h"
#include "kis_psd_layer_style.h"
void KisPSDTest::testFiles()
{
TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", QStringList());
}
void KisPSDTest::testOpening()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "testing_psd_ls.psd");
QScopedPointer<KisDocument> doc(qobject_cast<KisDocument*>(KisPart::instance()->createDocument()));
KisImportExportManager manager(doc.data());
manager.setBatchMode(true);
KisImportExportFilter::ConversionStatus status;
QString s = manager.importDocument(sourceFileInfo.absoluteFilePath(), QString(),
status);
- qDebug() << s;
+ dbgKrita << s;
Q_ASSERT(doc->image());
}
QSharedPointer<KisDocument> openPsdDocument(const QFileInfo &fileInfo)
{
QSharedPointer<KisDocument> doc(qobject_cast<KisDocument*>(KisPart::instance()->createDocument()));
KisImportExportManager manager(doc.data());
manager.setBatchMode(true);
KisImportExportFilter::ConversionStatus status;
QString s = manager.importDocument(fileInfo.absoluteFilePath(), QString(),
status);
return doc;
}
void KisPSDTest::testTransparencyMask()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "sources/masks.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
QImage result = doc->image()->projection()->convertToQImage(0, doc->image()->bounds());
QVERIFY(TestUtil::checkQImageExternal(result, "psd_test", "transparency_masks", "kiki_single"));
doc->setBackupFile(false);
doc->setOutputMimeType("image/vnd.adobe.photoshop");
QFileInfo dstFileInfo(QDir::currentPath() + QDir::separator() + "test_tmask.psd");
bool retval = doc->saveAs(KUrl(dstFileInfo.absoluteFilePath()));
QVERIFY(retval);
{
QSharedPointer<KisDocument> doc = openPsdDocument(dstFileInfo);
QVERIFY(doc->image());
QImage result = doc->image()->projection()->convertToQImage(0, doc->image()->bounds());
QVERIFY(TestUtil::checkQImageExternal(result, "psd_test", "transparency_masks", "kiki_single"));
QVERIFY(doc->image()->root()->lastChild());
QVERIFY(doc->image()->root()->lastChild()->firstChild());
QVERIFY(doc->image()->root()->lastChild()->firstChild()->inherits("KisTransparencyMask"));
}
}
void KisPSDTest::testOpenGrayscaleMultilayered()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "sources/gray.psd");
//QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "sources/100x100gray8.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
}
void KisPSDTest::testOpenGroupLayers()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "group_layers.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
KisNodeSP node = TestUtil::findNode(doc->image()->root(), "Group 1 PT");
KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(node.data());
QVERIFY(group);
QVERIFY(group->passThroughMode());
}
void KisPSDTest::testOpenLayerStyles()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "testing_psd_ls.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
KisLayerSP layer = dynamic_cast<KisLayer*>(doc->image()->root()->lastChild().data());
QVERIFY(layer->layerStyle());
QVERIFY(layer->layerStyle()->dropShadow());
QVERIFY(layer->layerStyle()->dropShadow()->effectEnabled());
}
void KisPSDTest::testOpenLayerStylesWithPattern()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "test_ls_pattern.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
KisLayerSP layer = dynamic_cast<KisLayer*>(doc->image()->root()->lastChild().data());
QVERIFY(layer->layerStyle());
QVERIFY(layer->layerStyle()->patternOverlay());
QVERIFY(layer->layerStyle()->patternOverlay()->effectEnabled());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern()->valid());
}
void KisPSDTest::testOpenLayerStylesWithPatternMulti()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "test_ls_pattern_multi.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
KisLayerSP layer = dynamic_cast<KisLayer*>(doc->image()->root()->lastChild().data());
QVERIFY(layer->layerStyle());
QVERIFY(layer->layerStyle()->patternOverlay());
QVERIFY(layer->layerStyle()->patternOverlay()->effectEnabled());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern()->valid());
QVERIFY(layer->layerStyle()->stroke());
QVERIFY(layer->layerStyle()->stroke()->effectEnabled());
QVERIFY(layer->layerStyle()->stroke()->pattern());
QVERIFY(layer->layerStyle()->stroke()->pattern()->valid());
}
void KisPSDTest::testSaveLayerStylesWithPatternMulti()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "test_ls_pattern_multi.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
KisLayerSP layer = dynamic_cast<KisLayer*>(doc->image()->root()->lastChild().data());
QVERIFY(layer->layerStyle());
QVERIFY(layer->layerStyle()->patternOverlay());
QVERIFY(layer->layerStyle()->patternOverlay()->effectEnabled());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern()->valid());
QVERIFY(layer->layerStyle()->stroke());
QVERIFY(layer->layerStyle()->stroke()->effectEnabled());
QVERIFY(layer->layerStyle()->stroke()->pattern());
QVERIFY(layer->layerStyle()->stroke()->pattern()->valid());
doc->setBackupFile(false);
doc->setOutputMimeType("image/vnd.adobe.photoshop");
QFileInfo dstFileInfo(QDir::currentPath() + QDir::separator() + "test_save_styles.psd");
bool retval = doc->saveAs(KUrl(dstFileInfo.absoluteFilePath()));
QVERIFY(retval);
{
QSharedPointer<KisDocument> doc = openPsdDocument(dstFileInfo);
QVERIFY(doc->image());
QImage result = doc->image()->projection()->convertToQImage(0, doc->image()->bounds());
//QVERIFY(TestUtil::checkQImageExternal(result, "psd_test", "transparency_masks", "kiki_single"));
KisLayerSP layer = dynamic_cast<KisLayer*>(doc->image()->root()->lastChild().data());
QVERIFY(layer->layerStyle());
QVERIFY(layer->layerStyle()->patternOverlay());
QVERIFY(layer->layerStyle()->patternOverlay()->effectEnabled());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern());
QVERIFY(layer->layerStyle()->patternOverlay()->pattern()->valid());
QVERIFY(layer->layerStyle()->stroke());
QVERIFY(layer->layerStyle()->stroke()->effectEnabled());
QVERIFY(layer->layerStyle()->stroke()->pattern());
QVERIFY(layer->layerStyle()->stroke()->pattern()->valid());
}
}
void KisPSDTest::testOpeningFromOpenCanvas()
{
QFileInfo sourceFileInfo(QString(FILES_DATA_DIR) + QDir::separator() + "test_krita_psd_from_opencanvas.psd");
Q_ASSERT(sourceFileInfo.exists());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
QVERIFY(doc->image());
QVERIFY(doc->image()->root()->firstChild());
}
void KisPSDTest::testOpeningAllFormats()
{
QString path = TestUtil::fetchExternalDataFileName("psd_format_test_files");
QDir dirSources(path);
foreach(QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
Q_ASSERT(sourceFileInfo.exists());
if (sourceFileInfo.isHidden() || sourceFileInfo.isDir()) {
continue;
}
if (sourceFileInfo.fileName() != "ml_cmyk_16b.psd") {
//continue;
}
- //qDebug() << "Opening" << ppVar(sourceFileInfo.fileName());
+ //dbgKrita << "Opening" << ppVar(sourceFileInfo.fileName());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
if (!doc->image()) {
- qCritical() << "FAILED to open" << sourceFileInfo.fileName();
+ errKrita << "FAILED to open" << sourceFileInfo.fileName();
continue;
}
// just check visually if the file loads fine
KIS_DUMP_DEVICE_2(doc->image()->projection(), QRect(0,0,100,100), sourceFileInfo.fileName(), "dd");
}
}
void KisPSDTest::testSavingAllFormats()
{
QString path = TestUtil::fetchExternalDataFileName("psd_format_test_files");
QDir dirSources(path);
foreach(QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
Q_ASSERT(sourceFileInfo.exists());
if (sourceFileInfo.isHidden() || sourceFileInfo.isDir()) {
continue;
}
if (sourceFileInfo.fileName() != "sl_rgb_8b.psd") {
//continue;
}
- qDebug() << "Opening" << ppVar(sourceFileInfo.fileName());
+ dbgKrita << "Opening" << ppVar(sourceFileInfo.fileName());
QSharedPointer<KisDocument> doc = openPsdDocument(sourceFileInfo);
if (!doc->image()) {
- qCritical() << "FAILED to open" << sourceFileInfo.fileName();
+ errKrita << "FAILED to open" << sourceFileInfo.fileName();
continue;
}
QString baseName = sourceFileInfo.fileName();
QString originalName = QString("%1_0orig").arg(baseName);
QString resultName = QString("%1_1result").arg(baseName);
QString tempPsdName = QString("%1_3interm.psd").arg(baseName);
QImage refImage = doc->image()->projection()->convertToQImage(0, QRect(0,0,100,100));
// uncomment to do a visual check
// KIS_DUMP_DEVICE_2(doc->image()->projection(), QRect(0,0,100,100), originalName, "dd");
doc->setBackupFile(false);
doc->setOutputMimeType("image/vnd.adobe.photoshop");
QFileInfo dstFileInfo(QDir::currentPath() + QDir::separator() + tempPsdName);
- qDebug() << "Saving" << ppVar(dstFileInfo.fileName());
+ dbgKrita << "Saving" << ppVar(dstFileInfo.fileName());
bool retval = doc->saveAs(KUrl(dstFileInfo.absoluteFilePath()));
QVERIFY(retval);
{
QSharedPointer<KisDocument> doc = openPsdDocument(dstFileInfo);
QVERIFY(doc->image());
// uncomment to do a visual check
//KIS_DUMP_DEVICE_2(doc->image()->projection(), QRect(0,0,100,100), resultName, "dd");
QImage resultImage = doc->image()->projection()->convertToQImage(0, QRect(0,0,100,100));
QCOMPARE(resultImage, refImage);
}
}
}
QTEST_KDEMAIN(KisPSDTest, GUI)
diff --git a/krita/plugins/formats/psd/tests/psd_utils_test.cpp b/krita/plugins/formats/psd/tests/psd_utils_test.cpp
index 3943215bb4f..6e6b0e86062 100644
--- a/krita/plugins/formats/psd/tests/psd_utils_test.cpp
+++ b/krita/plugins/formats/psd/tests/psd_utils_test.cpp
@@ -1,215 +1,217 @@
/*
* Copyright (C) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "psd_utils_test.h"
#include <QTest>
#include <QCoreApplication>
#include <klocale.h>
#include <qtest_kde.h>
#include "../../../../libpsd/psd_utils.h"
#include <QByteArray>
#include <QBuffer>
+#include <kis_debug.h>
+
void PSDUtilsTest::test_psdwrite_quint8()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
quint8 i = 3;
QVERIFY(psdwrite(&buf, i));
QCOMPARE(buf.buffer().size(), 1);
QCOMPARE(buf.buffer().at(0), '\3');
}
void PSDUtilsTest::test_psdwrite_quint16()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
quint16 i = 3;
QVERIFY(psdwrite(&buf, i));
QCOMPARE(buf.buffer().size(), 2);
QCOMPARE(buf.buffer().at(0), '\0');
QCOMPARE(buf.buffer().at(1), '\3');
}
void PSDUtilsTest::test_psdwrite_quint32()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
quint32 i = 100;
QVERIFY(psdwrite(&buf, i));
QCOMPARE(buf.buffer().size(), 4);
QCOMPARE(buf.buffer().at(0), '\0');
QCOMPARE(buf.buffer().at(1), '\0');
QCOMPARE(buf.buffer().at(2), '\0');
QCOMPARE(buf.buffer().at(3), 'd');
}
void PSDUtilsTest::test_psdwrite_qstring()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
QString s = "8BPS";
QVERIFY(psdwrite(&buf, s));
QCOMPARE(buf.buffer().size(), 4);
QCOMPARE(buf.buffer().at(0), '8');
QCOMPARE(buf.buffer().at(1), 'B');
QCOMPARE(buf.buffer().at(2), 'P');
QCOMPARE(buf.buffer().at(3), 'S');
}
void PSDUtilsTest::test_psdwrite_pascalstring()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
// test null string
QString s;
QVERIFY(psdwrite_pascalstring(&buf, s));
QCOMPARE(buf.buffer().size(), 2);
QCOMPARE(buf.buffer().at(0), '\0');
QCOMPARE(buf.buffer().at(1), '\0');
buf.close();
buf.buffer().clear();
buf.open(QBuffer::ReadWrite);
// test even string
s = QString("bl");
QVERIFY(psdwrite_pascalstring(&buf, s));
QCOMPARE(buf.buffer().size(), 3);
QCOMPARE(buf.buffer().at(0), '\2');
QCOMPARE(buf.buffer().at(1), 'b');
QCOMPARE(buf.buffer().at(2), 'l');
buf.close();
buf.buffer().clear();
buf.open(QBuffer::ReadWrite);
// test uneven string
s = QString("bla");
QVERIFY(psdwrite_pascalstring(&buf, s));
QCOMPARE(buf.buffer().size(), 5);
QCOMPARE(buf.buffer().at(0), '\3');
QCOMPARE(buf.buffer().at(1), 'b');
QCOMPARE(buf.buffer().at(2), 'l');
QCOMPARE(buf.buffer().at(3), 'a');
QCOMPARE(buf.buffer().at(4), '\0');
}
void PSDUtilsTest::test_psdpad()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
QVERIFY(psdpad(&buf, 6));
QCOMPARE(buf.buffer().size(), 6);
QCOMPARE(buf.buffer().at(0), '\0');
QCOMPARE(buf.buffer().at(1), '\0');
QCOMPARE(buf.buffer().at(2), '\0');
QCOMPARE(buf.buffer().at(3), '\0');
QCOMPARE(buf.buffer().at(4), '\0');
QCOMPARE(buf.buffer().at(5), '\0');
}
void PSDUtilsTest::test_psdread_quint8()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
quint8 s = 3;
QVERIFY(psdwrite(&buf, s));
buf.close();
buf.open(QBuffer::ReadOnly);
quint8 r;
QVERIFY(psdread(&buf, &r));
QCOMPARE(r, s);
}
void PSDUtilsTest::test_psdread_quint16()
{
QBuffer buf;
buf.open(QBuffer::ReadWrite);
quint16 s = 1024;
QVERIFY(psdwrite(&buf, s));
buf.close();
buf.open(QBuffer::ReadOnly);
quint16 r;
QVERIFY(psdread(&buf, &r));
QCOMPARE(r, s);
}
void PSDUtilsTest::test_psdread_quint32()
{
QBuffer buf;
QVERIFY(buf.open(QBuffer::ReadWrite));
quint32 s = 300000;
psdwrite(&buf, s);
buf.close();
buf.open(QBuffer::ReadOnly);
quint32 r;
QVERIFY(psdread(&buf, &r));
QCOMPARE(r, s);
}
void PSDUtilsTest::test_psdread_pascalstring()
{
QBuffer buf;
QString s;
QString r;
// test null string
buf.open(QBuffer::ReadWrite);
QVERIFY(psdwrite_pascalstring(&buf, s));
buf.close();
buf.open(QBuffer::ReadOnly);
psdread_pascalstring(&buf, r, 2);
QCOMPARE(r, s);
QVERIFY(buf.bytesAvailable() == 0);
// test even string
buf.close();
buf.buffer().clear();
r.clear();
buf.open(QBuffer::ReadWrite);
s = QString("bl");
QVERIFY(psdwrite_pascalstring(&buf, s));
buf.close();
buf.open(QBuffer::ReadOnly);
psdread_pascalstring(&buf, r, 1);
QCOMPARE(r, s);
QVERIFY(buf.bytesAvailable() == 0);
// test uneven string
buf.close();
buf.buffer().clear();
r.clear();
buf.open(QBuffer::ReadWrite);
s = QString("bla");
QVERIFY(psdwrite_pascalstring(&buf, s, 2));
buf.close();
buf.open(QBuffer::ReadOnly);
psdread_pascalstring(&buf, r, 2);
QCOMPARE(r, s);
- qDebug() << buf.bytesAvailable();
+ dbgKrita << buf.bytesAvailable();
QVERIFY(buf.bytesAvailable() == 0);
}
QTEST_KDEMAIN(PSDUtilsTest, GUI)
diff --git a/krita/plugins/formats/qml/qml_export.cc b/krita/plugins/formats/qml/qml_export.cc
index cf6addd5643..643e66183de 100644
--- a/krita/plugins/formats/qml/qml_export.cc
+++ b/krita/plugins/formats/qml/qml_export.cc
@@ -1,85 +1,85 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "qml_export.h"
#include <QCheckBox>
#include <QSlider>
#include <kpluginfactory.h>
#include <kurl.h>
#include <KisFilterChain.h>
#include <KisDocument.h>
#include <kis_image.h>
#include "qml_converter.h"
K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_qml_export.json", registerPlugin<QMLExport>();)
QMLExport::QMLExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
QMLExport::~QMLExport()
{
}
KisImportExportFilter::ConversionStatus QMLExport::convert(const QByteArray& from, const QByteArray& to)
{
Q_UNUSED(to);
if (from != "application/x-krita")
return KisImportExportFilter::NotImplemented;
KisDocument *input = m_chain->inputDocument();
QString filename = m_chain->outputFile();
- kDebug() << "input " << input;
+ dbgKrita << "input " << input;
if (!input) {
return KisImportExportFilter::NoDocumentCreated;
}
- kDebug() << "filename " << input;
+ dbgKrita << "filename " << input;
if (filename.isEmpty()) {
return KisImportExportFilter::FileNotFound;
}
KUrl url;
url.setPath(filename);
KisImageWSP image = input->image();
qApp->processEvents(); // For vector layers to be updated
image->waitForDone();
Q_CHECK_PTR(image);
QMLConverter converter;
KisImageBuilder_Result result = converter.buildFile(url, image);
if (result == KisImageBuilder_RESULT_OK) {
dbgFile << "success !";
return KisImportExportFilter::OK;
}
dbgFile << " Result =" << result;
return KisImportExportFilter::InternalError;
}
#include <qml_export.moc>
diff --git a/krita/plugins/formats/tga/kis_tga_import.cpp b/krita/plugins/formats/tga/kis_tga_import.cpp
index 86af74c9609..ff414d635d4 100644
--- a/krita/plugins/formats/tga/kis_tga_import.cpp
+++ b/krita/plugins/formats/tga/kis_tga_import.cpp
@@ -1,314 +1,314 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_tga_import.h"
#include <QCheckBox>
#include <QBuffer>
#include <QSlider>
#include <QApplication>
#include <kpluginfactory.h>
#include <kurl.h>
#include <KoColorSpace.h>
#include <KisFilterChain.h>
#include <KoColorSpaceRegistry.h>
#include <kis_transaction.h>
#include <kis_paint_device.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <tga.h>
K_PLUGIN_FACTORY_WITH_JSON(KisTGAImportFactory, "krita_tga_import.json", registerPlugin<KisTGAImport>();)
KisTGAImport::KisTGAImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisTGAImport::~KisTGAImport()
{
}
static QDataStream & operator>> (QDataStream & s, TgaHeader & head)
{
s >> head.id_length;
s >> head.colormap_type;
s >> head.image_type;
s >> head.colormap_index;
s >> head.colormap_length;
s >> head.colormap_size;
s >> head.x_origin;
s >> head.y_origin;
s >> head.width;
s >> head.height;
s >> head.pixel_size;
s >> head.flags;
- /*qDebug() << "id_length: " << head.id_length << " - colormap_type: " << head.colormap_type << " - image_type: " << head.image_type;
- qDebug() << "colormap_index: " << head.colormap_index << " - colormap_length: " << head.colormap_length << " - colormap_size: " << head.colormap_size;
- qDebug() << "x_origin: " << head.x_origin << " - y_origin: " << head.y_origin << " - width:" << head.width << " - height:" << head.height << " - pixelsize: " << head.pixel_size << " - flags: " << head.flags;*/
+ /*dbgKrita << "id_length: " << head.id_length << " - colormap_type: " << head.colormap_type << " - image_type: " << head.image_type;
+ dbgKrita << "colormap_index: " << head.colormap_index << " - colormap_length: " << head.colormap_length << " - colormap_size: " << head.colormap_size;
+ dbgKrita << "x_origin: " << head.x_origin << " - y_origin: " << head.y_origin << " - width:" << head.width << " - height:" << head.height << " - pixelsize: " << head.pixel_size << " - flags: " << head.flags;*/
return s;
}
static bool isSupported(const TgaHeader & head)
{
if (head.image_type != TGA_TYPE_INDEXED &&
head.image_type != TGA_TYPE_RGB &&
head.image_type != TGA_TYPE_GREY &&
head.image_type != TGA_TYPE_RLE_INDEXED &&
head.image_type != TGA_TYPE_RLE_RGB &&
head.image_type != TGA_TYPE_RLE_GREY) {
return false;
}
if (head.image_type == TGA_TYPE_INDEXED ||
head.image_type == TGA_TYPE_RLE_INDEXED) {
if (head.colormap_length > 256 || head.colormap_size != 24 || head.colormap_type != 1) {
return false;
}
}
if (head.image_type == TGA_TYPE_RGB ||
head.image_type == TGA_TYPE_GREY ||
head.image_type == TGA_TYPE_RLE_RGB ||
head.image_type == TGA_TYPE_RLE_GREY) {
if (head.colormap_type != 0) {
return false;
}
}
if (head.width == 0 || head.height == 0) {
return false;
}
if (head.pixel_size != 8 && head.pixel_size != 16 &&
head.pixel_size != 24 && head.pixel_size != 32) {
return false;
}
return true;
}
static bool loadTGA(QDataStream & s, const TgaHeader & tga, QImage &img)
{
// Create image.
img = QImage(tga.width, tga.height, QImage::Format_RGB32);
TgaHeaderInfo info(tga);
// Bits 0-3 are the numbers of alpha bits (can be zero!)
const int numAlphaBits = tga.flags & 0xf;
// However alpha exists only in the 32 bit format.
if ((tga.pixel_size == 32) && (tga.flags & 0xf)) {
img = QImage(tga.width, tga.height, QImage::Format_ARGB32);
}
uint pixel_size = (tga.pixel_size / 8);
uint size = tga.width * tga.height * pixel_size;
if (size < 1) {
- kDebug(399) << "This TGA file is broken with size " << size;
+ dbgFile << "This TGA file is broken with size " << size;
return false;
}
// Read palette.
char palette[768];
if (info.pal) {
// @todo Support palettes in other formats!
s.readRawData(palette, 3 * tga.colormap_length);
}
// Allocate image.
uchar * const image = new uchar[size];
if (info.rle) {
// Decode image.
char * dst = (char *)image;
int num = size;
while (num > 0) {
// Get packet header.
uchar c;
s >> c;
uint count = (c & 0x7f) + 1;
num -= count * pixel_size;
if (c & 0x80) {
// RLE pixels.
Q_ASSERT(pixel_size <= 8);
char pixel[8];
s.readRawData(pixel, pixel_size);
do {
memcpy(dst, pixel, pixel_size);
dst += pixel_size;
} while (--count);
} else {
// Raw pixels.
count *= pixel_size;
s.readRawData(dst, count);
dst += count;
}
}
} else {
// Read raw image.
s.readRawData((char *)image, size);
}
// Convert image to internal format.
int y_start, y_step, y_end;
if (tga.flags & TGA_ORIGIN_UPPER) {
y_start = 0;
y_step = 1;
y_end = tga.height;
} else {
y_start = tga.height - 1;
y_step = -1;
y_end = -1;
}
uchar* src = image;
for (int y = y_start; y != y_end; y += y_step) {
QRgb * scanline = (QRgb *) img.scanLine(y);
if (info.pal) {
// Paletted.
for (int x = 0; x < tga.width; x++) {
uchar idx = *src++;
scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]);
}
} else if (info.grey) {
// Greyscale.
for (int x = 0; x < tga.width; x++) {
scanline[x] = qRgb(*src, *src, *src);
src++;
}
} else {
// True Color.
if (tga.pixel_size == 16) {
for (int x = 0; x < tga.width; x++) {
Color555 c = *reinterpret_cast<Color555 *>(src);
scanline[x] = qRgb((c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2));
src += 2;
}
} else if (tga.pixel_size == 24) {
for (int x = 0; x < tga.width; x++) {
scanline[x] = qRgb(src[2], src[1], src[0]);
src += 3;
}
} else if (tga.pixel_size == 32) {
for (int x = 0; x < tga.width; x++) {
// ### TODO: verify with images having really some alpha data
const uchar alpha = (src[3] << (8 - numAlphaBits));
scanline[x] = qRgba(src[2], src[1], src[0], alpha);
src += 4;
}
}
}
}
// Free image.
delete []image;
return true;
}
KisImportExportFilter::ConversionStatus KisTGAImport::convert(const QByteArray& from, const QByteArray& to)
{
dbgFile << "TGA import! From:" << from << ", To:" << to << 0;
if (to != "application/x-krita")
return KisImportExportFilter::BadMimeType;
KisDocument * doc = m_chain->outputDocument();
if (!doc)
return KisImportExportFilter::NoDocumentCreated;
QString filename = m_chain->inputFile();
doc->prepareForImport();
if (!filename.isEmpty()) {
KUrl url(filename);
if (url.isEmpty())
return KisImportExportFilter::FileNotFound;
if (!url.isLocalFile()) {
return KisImportExportFilter::FileNotFound;
}
QString localFile = url.toLocalFile();
QFile f(localFile);
f.open(QIODevice::ReadOnly);
QDataStream s(&f);
s.setByteOrder(QDataStream::LittleEndian);
TgaHeader tga;
s >> tga;
s.device()->seek(TgaHeader::SIZE + tga.id_length);
// Check image file format.
if (s.atEnd()) {
return KisImportExportFilter::InvalidFormat;
}
// Check supported file types.
if (!isSupported(tga)) {
return KisImportExportFilter::InvalidFormat;
}
QImage img;
bool result = loadTGA(s, tga, img);
if (result == false) {
return KisImportExportFilter::CreationError;
}
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(doc->createUndoStore(), img.width(), img.height(), colorSpace, "imported from tga");
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), 255);
layer->paintDevice()->convertFromQImage(img, 0, 0, 0);
image->addNode(layer.data(), image->rootLayer().data());
doc->setCurrentImage(image);
return KisImportExportFilter::OK;
}
return KisImportExportFilter::StorageCreationError;
}
#include "kis_tga_import.moc"
diff --git a/krita/plugins/formats/tiff/kis_dlg_options_tiff.cpp b/krita/plugins/formats/tiff/kis_dlg_options_tiff.cpp
index 87dee555968..45da8970c17 100644
--- a/krita/plugins/formats/tiff/kis_dlg_options_tiff.cpp
+++ b/krita/plugins/formats/tiff/kis_dlg_options_tiff.cpp
@@ -1,167 +1,167 @@
/*
* Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dlg_options_tiff.h"
#include <QCheckBox>
#include <QGroupBox>
#include <QSlider>
#include <QStackedWidget>
#include <QApplication>
#include <kcombobox.h>
#include <klocale.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include "ui_kis_wdg_options_tiff.h"
KisDlgOptionsTIFF::KisDlgOptionsTIFF(QWidget *parent)
: KDialog(parent), wdg(new QWidget)
{
setWindowTitle(i18n("TIFF Export Options"));
setButtons(KDialog::Ok | KDialog::Cancel);
optionswdg = new Ui_KisWdgOptionsTIFF();
optionswdg->setupUi(wdg);
activated(0);
connect(optionswdg->kComboBoxCompressionType, SIGNAL(activated(int)), this, SLOT(activated(int)));
connect(optionswdg->flatten, SIGNAL(toggled(bool)), this, SLOT(flattenToggled(bool)));
setMainWidget(wdg);
QApplication::restoreOverrideCursor();
setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
QString filterConfig = KisConfig().exportConfiguration("TIFF");
KisPropertiesConfiguration cfg;
cfg.fromXML(filterConfig);
optionswdg->kComboBoxCompressionType->setCurrentIndex(cfg.getInt("compressiontype", 0));
activated(optionswdg->kComboBoxCompressionType->currentIndex());
optionswdg->kComboBoxPredictor->setCurrentIndex(cfg.getInt("predictor", 0));
optionswdg->alpha->setChecked(cfg.getBool("alpha", true));
optionswdg->flatten->setChecked(cfg.getBool("flatten", true));
flattenToggled(optionswdg->flatten->isChecked());
optionswdg->qualityLevel->setValue(cfg.getInt("quality", 80));
optionswdg->compressionLevelDeflate->setValue(cfg.getInt("deflate", 6));
optionswdg->kComboBoxFaxMode->setCurrentIndex(cfg.getInt("faxmode", 0));
optionswdg->compressionLevelPixarLog->setValue(cfg.getInt("pixarlog", 6));
}
KisDlgOptionsTIFF::~KisDlgOptionsTIFF()
{
delete optionswdg;
}
void KisDlgOptionsTIFF::activated(int index)
{
/* optionswdg->groupBoxJPEG->hide();
optionswdg->groupBoxDeflate->hide();
optionswdg->groupBoxCCITGroupCCITG3->hide();
optionswdg->groupBoxPixarLog->hide();*/
switch (index) {
case 1:
optionswdg->codecsOptionsStack->setCurrentIndex(1);
// optionswdg->groupBoxJPEG->show();
break;
case 2:
optionswdg->codecsOptionsStack->setCurrentIndex(2);
// optionswdg->groupBoxDeflate->show();
break;
case 6:
optionswdg->codecsOptionsStack->setCurrentIndex(3);
// optionswdg->groupBoxCCITGroupCCITG3->show();
break;
case 8:
optionswdg->codecsOptionsStack->setCurrentIndex(4);
// optionswdg->groupBoxPixarLog->show();
break;
default:
optionswdg->codecsOptionsStack->setCurrentIndex(0);
}
}
void KisDlgOptionsTIFF::flattenToggled(bool t)
{
optionswdg->alpha->setEnabled(t);
if (!t) {
optionswdg->alpha->setChecked(true);
}
}
KisTIFFOptions KisDlgOptionsTIFF::options()
{
KisTIFFOptions options;
switch (optionswdg->kComboBoxCompressionType->currentIndex()) {
case 0:
options.compressionType = COMPRESSION_NONE;
break;
case 1:
options.compressionType = COMPRESSION_JPEG;
break;
case 2:
options.compressionType = COMPRESSION_DEFLATE;
break;
case 3:
options.compressionType = COMPRESSION_LZW;
break;
case 4:
options.compressionType = COMPRESSION_JP2000;
break;
case 5:
options.compressionType = COMPRESSION_CCITTRLE;
break;
case 6:
options.compressionType = COMPRESSION_CCITTFAX3;
break;
case 7:
options.compressionType = COMPRESSION_CCITTFAX4;
break;
case 8:
options.compressionType = COMPRESSION_PIXARLOG;
break;
default:
options.compressionType = COMPRESSION_NONE;
}
options.predictor = optionswdg->kComboBoxPredictor->currentIndex() + 1;
options.alpha = optionswdg->alpha->isChecked();
options.flatten = optionswdg->flatten->isChecked();
options.jpegQuality = optionswdg->qualityLevel->value();
options.deflateCompress = optionswdg->compressionLevelDeflate->value();
options.faxMode = optionswdg->kComboBoxFaxMode->currentIndex() + 1;
options.pixarLogCompress = optionswdg->compressionLevelPixarLog->value();
- qDebug() << options.compressionType << options.predictor << options.alpha << options.jpegQuality << options.deflateCompress << options.faxMode << options.pixarLogCompress;
+ dbgKrita << options.compressionType << options.predictor << options.alpha << options.jpegQuality << options.deflateCompress << options.faxMode << options.pixarLogCompress;
KisPropertiesConfiguration cfg;
cfg.setProperty("compressiontype", optionswdg->kComboBoxCompressionType->currentIndex());
cfg.setProperty("predictor", options.predictor - 1);
cfg.setProperty("alpha", options.alpha);
cfg.setProperty("flatten", options.flatten);
cfg.setProperty("quality", options.jpegQuality);
cfg.setProperty("deflate", options.deflateCompress);
cfg.setProperty("faxmode", options.faxMode - 1);
cfg.setProperty("pixarlog", options.pixarLogCompress);
KisConfig().setExportConfiguration("TIFF", cfg);
return options;
}
diff --git a/krita/plugins/formats/tiff/kis_tiff_writer_visitor.cpp b/krita/plugins/formats/tiff/kis_tiff_writer_visitor.cpp
index e5dbb1b4ca9..b3f02bd8908 100644
--- a/krita/plugins/formats/tiff/kis_tiff_writer_visitor.cpp
+++ b/krita/plugins/formats/tiff/kis_tiff_writer_visitor.cpp
@@ -1,271 +1,271 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_tiff_writer_visitor.h"
#include <QMessageBox>
#include <klocale.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoID.h>
#include <kis_annotation.h>
#include <kis_paint_device.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_types.h>
#include <generator/kis_generator_layer.h>
#include "kis_tiff_converter.h"
#include <kis_iterator_ng.h>
#include <kis_shape_layer.h>
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
namespace
{
bool writeColorSpaceInformation(TIFF* image, const KoColorSpace * cs, uint16& color_type, uint16& sample_format)
{
- qDebug() << cs->id();
+ dbgKrita << cs->id();
if (cs->id() == "GRAYA" || cs->id() == "GRAYAU16") {
color_type = PHOTOMETRIC_MINISBLACK;
return true;
}
if (KoID(cs->id()) == KoID("RGBA") || KoID(cs->id()) == KoID("RGBA16")) {
color_type = PHOTOMETRIC_RGB;
return true;
}
if (KoID(cs->id()) == KoID("RGBAF16") || KoID(cs->id()) == KoID("RGBAF32")) {
color_type = PHOTOMETRIC_RGB;
sample_format = SAMPLEFORMAT_IEEEFP;
return true;
}
if (cs->id() == "CMYK" || cs->id() == "CMYKAU16") {
color_type = PHOTOMETRIC_SEPARATED;
TIFFSetField(image, TIFFTAG_INKSET, INKSET_CMYK);
return true;
}
if (cs->id() == "LABA") {
color_type = PHOTOMETRIC_CIELAB;
return true;
}
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Cannot export images in %1.\n", cs->name())) ;
return false;
}
}
KisTIFFWriterVisitor::KisTIFFWriterVisitor(TIFF*image, KisTIFFOptions* options)
: m_image(image)
, m_options(options)
{
}
KisTIFFWriterVisitor::~KisTIFFWriterVisitor()
{
}
bool KisTIFFWriterVisitor::saveAlpha()
{
return m_options->alpha;
}
bool KisTIFFWriterVisitor::copyDataToStrips(KisHLineConstIteratorSP it, tdata_t buff, uint8 depth, uint16 sample_format, uint8 nbcolorssamples, quint8* poses)
{
if (depth == 32) {
Q_ASSERT(sample_format == SAMPLEFORMAT_IEEEFP);
float *dst = reinterpret_cast<float *>(buff);
do {
const float *d = reinterpret_cast<const float *>(it->oldRawData());
int i;
for (i = 0; i < nbcolorssamples; i++) {
*(dst++) = d[poses[i]];
}
if (saveAlpha()) *(dst++) = d[poses[i]];
} while (it->nextPixel());
return true;
}
else if (depth == 16 ) {
if (sample_format == SAMPLEFORMAT_IEEEFP) {
#ifdef HAVE_OPENEXR
half *dst = reinterpret_cast<half *>(buff);
do {
const half *d = reinterpret_cast<const half *>(it->oldRawData());
int i;
for (i = 0; i < nbcolorssamples; i++) {
*(dst++) = d[poses[i]];
}
if (saveAlpha()) *(dst++) = d[poses[i]];
} while (it->nextPixel());
return true;
#endif
}
else {
quint16 *dst = reinterpret_cast<quint16 *>(buff);
do {
const quint16 *d = reinterpret_cast<const quint16 *>(it->oldRawData());
int i;
for (i = 0; i < nbcolorssamples; i++) {
*(dst++) = d[poses[i]];
}
if (saveAlpha()) *(dst++) = d[poses[i]];
} while (it->nextPixel());
return true;
}
}
else if (depth == 8) {
quint8 *dst = reinterpret_cast<quint8 *>(buff);
do {
const quint8 *d = it->oldRawData();
int i;
for (i = 0; i < nbcolorssamples; i++) {
*(dst++) = d[poses[i]];
}
if (saveAlpha()) *(dst++) = d[poses[i]];
} while (it->nextPixel());
return true;
}
return false;
}
bool KisTIFFWriterVisitor::visit(KisPaintLayer *layer)
{
return saveLayerProjection(layer);
}
bool KisTIFFWriterVisitor::visit(KisGroupLayer *layer)
{
dbgFile << "Visiting on grouplayer" << layer->name() << "";
return visitAll(layer, true);
}
bool KisTIFFWriterVisitor::visit(KisGeneratorLayer* layer)
{
// a generator layer has a nice paint device we can save.
return saveLayerProjection(layer);
}
bool KisTIFFWriterVisitor::visit(KisExternalLayer* layer)
{
// a shape layer has a nice paint device we can save.
if (qobject_cast<KisShapeLayer*>(layer)) {
return saveLayerProjection(layer);
}
return true;
}
bool KisTIFFWriterVisitor::saveLayerProjection(KisLayer * layer)
{
dbgFile << "visiting on layer" << layer->name() << "";
KisPaintDeviceSP pd = layer->projection();
// Save depth
int depth = 8 * pd->pixelSize() / pd->channelCount();
TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth);
// Save number of samples
if (saveAlpha()) {
TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount());
uint16 sampleinfo[1] = { EXTRASAMPLE_UNASSALPHA };
TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
} else {
TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1);
TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0);
}
// Save colorspace information
uint16 color_type;
uint16 sample_format = SAMPLEFORMAT_UINT;
if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format)) { // unsupported colorspace
return false;
}
TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type);
TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format);
TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width());
TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height());
// Set the compression options
TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType);
TIFFSetField(image(), TIFFTAG_FAXMODE, m_options->faxMode);
TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality);
TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress);
TIFFSetField(image(), TIFFTAG_PIXARLOGQUALITY, m_options->pixarLogCompress);
// Set the predictor
TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor);
// Use contiguous configuration
TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
// Use 8 rows per strip
TIFFSetField(image(), TIFFTAG_ROWSPERSTRIP, 8);
// Save profile
const KoColorProfile* profile = pd->colorSpace()->profile();
if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) {
QByteArray ba = profile->rawData();
TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.data());
}
tsize_t stripsize = TIFFStripSize(image());
tdata_t buff = _TIFFmalloc(stripsize);
qint32 height = layer->image()->height();
qint32 width = layer->image()->width();
bool r = true;
for (int y = 0; y < height; y++) {
KisHLineConstIteratorSP it = pd->createHLineConstIteratorNG(0, y, width);
switch (color_type) {
case PHOTOMETRIC_MINISBLACK: {
quint8 poses[] = { 0, 1 };
r = copyDataToStrips(it, buff, depth, sample_format, 1, poses);
}
break;
case PHOTOMETRIC_RGB: {
quint8 poses[4];
if (sample_format == SAMPLEFORMAT_IEEEFP) {
poses[2] = 2; poses[1] = 1; poses[0] = 0; poses[3] = 3;
} else {
poses[0] = 2; poses[1] = 1; poses[2] = 0; poses[3] = 3;
}
r = copyDataToStrips(it, buff, depth, sample_format, 3, poses);
}
break;
case PHOTOMETRIC_SEPARATED: {
quint8 poses[] = { 0, 1, 2, 3, 4 };
r = copyDataToStrips(it, buff, depth, sample_format, 4, poses);
}
break;
case PHOTOMETRIC_CIELAB: {
quint8 poses[] = { 0, 1, 2, 3 };
r = copyDataToStrips(it, buff, depth, sample_format, 3, poses);
}
break;
return false;
}
if (!r) return false;
TIFFWriteScanline(image(), buff, y, (tsample_t) - 1);
}
_TIFFfree(buff);
TIFFWriteDirectory(image());
return true;
}
diff --git a/krita/plugins/formats/tiff/tests/kis_tiff_test.cpp b/krita/plugins/formats/tiff/tests/kis_tiff_test.cpp
index 23eb03060c8..e32615d41ac 100644
--- a/krita/plugins/formats/tiff/tests/kis_tiff_test.cpp
+++ b/krita/plugins/formats/tiff/tests/kis_tiff_test.cpp
@@ -1,115 +1,115 @@
/*
* Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tiff_test.h"
#include <QTest>
#include <QCoreApplication>
#include <qtest_kde.h>
#include "filestest.h"
#include <KoColorModelStandardIds.h>
#include <KoColor.h>
#include "kisexiv2/kis_exiv2.h"
#ifndef FILES_DATA_DIR
#error "FILES_DATA_DIR not set. A directory with the data used for testing the importing of files in krita"
#endif
void KisTiffTest::testFiles()
{
// XXX: make the exiv io backends real plugins
KisExiv2::initialize();
QStringList excludes;
#ifndef CPU_32_BITS
excludes << "flower-minisblack-06.tif";
#endif
#ifdef HAVE_LCMS2
excludes << "flower-separated-contig-08.tif"
<< "flower-separated-contig-16.tif"
<< "flower-separated-planar-08.tif"
<< "flower-separated-planar-16.tif"
<< "flower-minisblack-10.tif"
<< "flower-minisblack-12.tif"
<< "flower-minisblack-14.tif"
<< "flower-minisblack-16.tif"
<< "flower-minisblack-24.tif"
<< "flower-minisblack-32.tif"
<< "strike.tif";
#endif
excludes << "text.tif" << "ycbcr-cat.tif";
TestUtil::testFiles(QString(FILES_DATA_DIR) + "/sources", excludes);
}
void KisTiffTest::testRoundTripRGBF16()
{
// Disabled for now, it's broken because we assumed integers.
#if 0
QRect testRect(0,0,1000,1000);
QRect fillRect(100,100,100,100);
const KoColorSpace *csf16 = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id(), 0);
KisDocument *doc0 = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
doc0->newImage("test", testRect.width(), testRect.height(), csf16, KoColor(Qt::blue, csf16), QString(), 1.0);
QTemporaryFile tmpFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(".tiff"));
tmpFile.open();
doc0->setBackupFile(false);
doc0->setOutputMimeType("image/tiff");
doc0->setSaveInBatchMode(true);
doc0->saveAs(KUrl("file://" + tmpFile.fileName()));
KisNodeSP layer0 = doc0->image()->root()->firstChild();
Q_ASSERT(layer0);
layer0->paintDevice()->fill(fillRect, KoColor(Qt::red, csf16));
KisDocument *doc1 = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
KisImportExportManager manager(doc1);
manager.setBatchMode(false);
KisImportExportFilter::ConversionStatus status;
QString s = manager.importDocument(tmpFile.fileName(),
QString(),
status);
- qDebug() << s;
+ dbgKrita << s;
Q_ASSERT(doc1->image());
QImage ref0 = doc0->image()->projection()->convertToQImage(0, testRect);
QImage ref1 = doc1->image()->projection()->convertToQImage(0, testRect);
QCOMPARE(ref0, ref1);
#endif
}
QTEST_KDEMAIN(KisTiffTest, GUI)
diff --git a/krita/plugins/paintops/chalk/kis_chalk_paintop_settings.cpp b/krita/plugins/paintops/chalk/kis_chalk_paintop_settings.cpp
index 5b1edddafe6..f6cfd67232a 100644
--- a/krita/plugins/paintops/chalk/kis_chalk_paintop_settings.cpp
+++ b/krita/plugins/paintops/chalk/kis_chalk_paintop_settings.cpp
@@ -1,69 +1,69 @@
/*
* Copyright (c) 2008 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_chalk_paintop_settings.h"
#include <kis_chalkop_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option.h>
KisChalkPaintOpSettings::KisChalkPaintOpSettings()
{
}
bool KisChalkPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
bool KisChalkPaintOpSettings::isAirbrushing() const
{
return getBool(AIRBRUSH_ENABLED);
}
int KisChalkPaintOpSettings::rate() const
{
return getInt(AIRBRUSH_RATE);
}
QPainterPath KisChalkPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) {
qreal size = getInt(CHALK_RADIUS) * 2 + 1;
path = ellipseOutline(size, size, 1.0, 0.0);
QPainterPath tiltLine;
QLineF tiltAngle(QPointF(0.0,0.0), QPointF(0.0,size));
- tiltAngle.setLength(qMax(size*0.5, qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
+ tiltAngle.setLength(qMax(size*qreal(0.5), qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0);
tiltLine.moveTo(tiltAngle.p1());
tiltLine.lineTo(tiltAngle.p2());
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0);
tiltLine.lineTo(tiltAngle.p2());
tiltLine.lineTo(tiltAngle.p1());
if (mode == CursorTiltOutline) {
path.addPath(tiltLine);
}
path.translate(info.pos());
}
return path;
}
diff --git a/krita/plugins/paintops/curvebrush/curve_brush.cpp b/krita/plugins/paintops/curvebrush/curve_brush.cpp
index c69a0e000d6..347cc317e46 100644
--- a/krita/plugins/paintops/curvebrush/curve_brush.cpp
+++ b/krita/plugins/paintops/curvebrush/curve_brush.cpp
@@ -1,179 +1,179 @@
/*
* Copyright (c) 2008,2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "curve_brush.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <kis_random_accessor_ng.h>
#include <cmath>
#include <ctime>
#include "kis_curve_line_option.h"
#if defined(_WIN32) || defined(_WIN64)
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
CurveBrush::CurveBrush() :
m_painter(0),
m_branch(0)
{
srand48(time(0));
m_pens.reserve(1024);
}
CurveBrush::~CurveBrush()
{
delete m_painter;
}
QPointF CurveBrush::getLinearBezier(const QPointF &p1, const QPointF &p2, qreal u)
{
qreal rx = (1.0 - u) * p1.x() + u * p2.x();
qreal ry = (1.0 - u) * p1.y() + u * p2.y();
return QPointF(rx, ry);
}
QPointF CurveBrush::getQuadraticBezier(const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal u)
{
qreal rx =
pow((1.0 - u), 2) * p0.x() +
2 * u * (1.0 - u) * p1.x() +
pow(u, 2) * p2.x();
qreal ry =
pow((1.0 - u), 2) * p0.y() +
2 * u * (1.0 - u) * p1.y() +
pow(u, 2) * p2.y();
return QPointF(rx, ry);
}
QPointF CurveBrush::getCubicBezier(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, qreal u)
{
qreal rx =
pow((1.0 - u), 3) * p0.x() +
3.0 * u * pow((1.0 - u), 2) * p1.x() +
3.0 * pow(u, 2) * (1.0 - u) * p2.x() +
pow(u, 3) * p3.x();
qreal ry =
pow((1.0 - u), 3) * p0.y() +
3.0 * u * pow((1.0 - u), 2) * p1.y() +
3.0 * pow(u, 2) * (1.0 - u) * p2.y() +
pow(u, 3) * p3.y();
return QPointF(rx, ry);
}
void CurveBrush::putPixel(QPointF pos, KoColor &color)
{
int ipx = int (pos.x());
int ipy = int (pos.y());
qreal fx = pos.x() - ipx;
qreal fy = pos.y() - ipy;
qreal btl = (1 - fx) * (1 - fy);
qreal btr = (fx) * (1 - fy);
qreal bbl = (1 - fx) * (fy);
qreal bbr = (fx) * (fy);
color.setOpacity(btl);
m_writeAccessor->moveTo(ipx , ipy);
if (cs->opacityU8(m_writeAccessor->rawData()) < color.opacityU8()) {
memcpy(m_writeAccessor->rawData(), color.data(), m_pixelSize);
}
color.setOpacity(btr);
m_writeAccessor->moveTo(ipx + 1, ipy);
if (cs->opacityU8(m_writeAccessor->rawData()) < color.opacityU8()) {
memcpy(m_writeAccessor->rawData(), color.data(), m_pixelSize);
}
color.setOpacity(bbl);
m_writeAccessor->moveTo(ipx, ipy + 1);
if (cs->opacityU8(m_writeAccessor->rawData()) < color.opacityU8()) {
memcpy(m_writeAccessor->rawData(), color.data(), m_pixelSize);
}
color.setOpacity(bbr);
m_writeAccessor->moveTo(ipx + 1, ipy + 1);
if (cs->opacityU8(m_writeAccessor->rawData()) < color.opacityU8()) {
memcpy(m_writeAccessor->rawData(), color.data(), m_pixelSize);
}
}
void CurveBrush::strokePens(QPointF pi1, QPointF pi2, KisPainter &/*painter*/)
{
if (m_pens.isEmpty()) {
m_pens.append(Pen(pi1, 0.0, 1.0));
}
qreal dx = pi2.x() - pi1.x();
qreal dy = pi2.y() - pi1.y();
for (int i = 0; i < m_pens.length(); i++) {
Pen &pen = m_pens[i];
QPointF endPoint(dx, dy);
QPainterPath path;
path.moveTo(0, 0);
path.lineTo(dx, dy);
QTransform transform;
transform.reset();
transform.translate(pen.pos.x(), pen.pos.y());
transform.scale(pen.scale, pen.scale);
transform.rotate(pen.rotation);
path = transform.map(path);
//m_painter->drawPainterPath(path, QPen(Qt::white, 1.0));
endPoint = transform.map(endPoint);
m_painter->drawThickLine(pen.pos, endPoint, 1.0, 1.0);
pen.pos = endPoint;
}
qreal branchThreshold = 0.5;
if ((m_branch * drand48() > branchThreshold) && (m_pens.length() < 1024)) {
int index = floor(drand48() * (m_pens.length() - 1));
m_newPen.pos = m_pens.at(index).pos;
m_newPen.rotation = drand48() * M_PI / 32; //atan(dy/dx) + (drand48() - 0.5) * M_PI/32;
m_newPen.scale = drand48() * m_pens.at(index).scale;
m_pens.append(m_newPen);
- qDebug() << m_pens.length();
+ dbgKrita << m_pens.length();
m_branch = 0;
}
else {
m_branch++;
}
}
diff --git a/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_p.h b/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_p.h
index d12006c6125..f937a91f573 100644
--- a/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_p.h
+++ b/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_p.h
@@ -1,67 +1,67 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004-2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DUPLICATEOP_P_H_
#define KIS_DUPLICATEOP_P_H_
namespace DuplicateOpUtils {
inline qreal minimizeEnergy(const qreal* m, qreal* sol, int w, int h)
{
qreal err = 0;
if (h < 3 || w < 3) {
int size = 3 * w * h;
for (int i = 0; i < size; i++) {
sol[i] = 1.0;
}
- qWarning() << "WARNING: healing width or height are smaller than 3 px. The result will have artifacts!";
+ warnKrita << "WARNING: healing width or height are smaller than 3 px. The result will have artifacts!";
} else {
int rowstride = 3 * w;
memcpy(sol, m, 3 * sizeof(qreal) * w);
m += rowstride;
sol += rowstride;
for (int i = 1; i < h - 1; i++) {
memcpy(sol, m, 3 * sizeof(qreal));
m += 3; sol += 3;
for (int j = 3; j < rowstride - 3; j++) {
qreal tmp = *sol;
*sol = ((*(m - 3) + * (m + 3) + * (m - rowstride) + * (m + rowstride)) + 2 * *m) / 6;
qreal diff = *sol - tmp;
err += diff * diff;
m ++; sol ++;
}
memcpy(sol, m, 3 * sizeof(qreal));
m += 3; sol += 3;
}
memcpy(sol, m, 3 * sizeof(qreal) * w);
}
return err;
}
}
#endif // KIS_DUPLICATEOP_P_H_
diff --git a/krita/plugins/paintops/gridbrush/kis_grid_paintop.cpp b/krita/plugins/paintops/gridbrush/kis_grid_paintop.cpp
index b9ddd93f183..d98998232fa 100644
--- a/krita/plugins/paintops/gridbrush/kis_grid_paintop.cpp
+++ b/krita/plugins/paintops/gridbrush/kis_grid_paintop.cpp
@@ -1,256 +1,256 @@
/*
* Copyright (c) 2009,2010 Lukáš Tvrdý (lukast.dev@gmail.com)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_grid_paintop.h"
#include "kis_grid_paintop_settings.h"
#include <cmath>
#include <QtGlobal>
#include <QRect>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_types.h>
#include <kis_paintop.h>
#include <kis_paint_information.h>
#include <kis_cross_device_color_picker.h>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoMixColorsOp.h>
#include <kis_gridop_option.h>
#include <kis_grid_shape_option.h>
#include <kis_color_option.h>
#ifdef BENCHMARK
#include <QTime>
#endif
#ifdef Q_OS_WIN
// quoting DRAND48(3) man-page:
// These functions are declared obsolete by SVID 3,
// which states that rand(3) should be used instead.
#define drand48() (static_cast<double>(qrand()) / static_cast<double>(RAND_MAX))
#endif
KisGridPaintOp::KisGridPaintOp(const KisGridPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image)
: KisPaintOp(painter)
, m_settings(settings)
, m_image(image)
, m_node(node)
{
m_properties.fillProperties(settings);
m_colorProperties.fillProperties(settings);
m_xSpacing = m_properties.gridWidth * m_properties.scale;
m_ySpacing = m_properties.gridHeight * m_properties.scale;
m_spacing = m_xSpacing;
m_dab = source()->createCompositionSourceDevice();
m_painter = new KisPainter(m_dab);
m_painter->setPaintColor(painter->paintColor());
m_painter->setFillStyle(KisPainter::FillStyleForegroundColor);
#ifdef BENCHMARK
m_count = m_total = 0;
#endif
}
KisGridPaintOp::~KisGridPaintOp()
{
delete m_painter;
}
KisSpacingInformation KisGridPaintOp::paintAt(const KisPaintInformation& info)
{
#ifdef BENCHMARK
QTime time;
time.start();
#endif
if (!painter()) return KisSpacingInformation(m_spacing);
m_dab->clear();
int gridWidth = m_properties.gridWidth * m_properties.scale;
int gridHeight = m_properties.gridHeight * m_properties.scale;
int divide;
if (m_properties.pressureDivision) {
divide = m_properties.divisionLevel * info.pressure();
}
else {
divide = m_properties.divisionLevel;
}
divide = qRound(m_properties.scale * divide);
int posX = qRound(info.pos().x());
int posY = qRound(info.pos().y());
QPoint dabPosition(posX - posX % gridWidth, posY - posY % gridHeight);
QPoint dabRightBottom(dabPosition.x() + gridWidth, dabPosition.y() + gridHeight);
divide = qMax(1, divide);
qreal yStep = gridHeight / (qreal)divide;
qreal xStep = gridWidth / (qreal)divide;
QRectF tile;
KoColor color(painter()->paintColor());
KisCrossDeviceColorPicker *colorPicker = 0;
if (m_node) {
colorPicker = new KisCrossDeviceColorPicker(m_node->paintDevice(), color);
}
qreal vertBorder = m_properties.vertBorder;
qreal horzBorder = m_properties.horizBorder;
if (m_properties.randomBorder) {
if (vertBorder == horzBorder) {
vertBorder = horzBorder = vertBorder * drand48();
}
else {
vertBorder *= drand48();
horzBorder *= drand48();
}
}
bool shouldColor = true;
// fill the tile
if (m_colorProperties.fillBackground) {
m_dab->fill(dabPosition.x(), dabPosition.y(), gridWidth, gridHeight, painter()->backgroundColor().data());
}
for (int y = 0; y < divide; y++) {
for (int x = 0; x < divide; x++) {
// determine the tile size
tile = QRectF(dabPosition.x() + x * xStep, dabPosition.y() + y * yStep, xStep, yStep);
tile.adjust(vertBorder, horzBorder, -vertBorder, -horzBorder);
tile = tile.normalized();
// do color transformation
if (shouldColor) {
if (colorPicker && m_colorProperties.sampleInputColor) {
colorPicker->pickOldColor(tile.center().x(), tile.center().y(), color.data());
}
// mix the color with background color
if (m_colorProperties.mixBgColor) {
KoMixColorsOp * mixOp = source()->colorSpace()->mixColorsOp();
const quint8 *colors[2];
colors[0] = color.data();
colors[1] = painter()->backgroundColor().data();
qint16 colorWeights[2];
int MAX_16BIT = 255;
qreal blend = info.pressure();
colorWeights[0] = static_cast<quint16>(blend * MAX_16BIT);
colorWeights[1] = static_cast<quint16>((1.0 - blend) * MAX_16BIT);
mixOp->mixColors(colors, colorWeights, 2, color.data());
}
if (m_colorProperties.useRandomHSV) {
QHash<QString, QVariant> params;
params["h"] = (m_colorProperties.hue / 180.0) * drand48();
params["s"] = (m_colorProperties.saturation / 100.0) * drand48();
params["v"] = (m_colorProperties.value / 100.0) * drand48();
KoColorTransformation* transfo;
transfo = m_dab->colorSpace()->createColorTransformation("hsv_adjustment", params);
transfo->setParameter(3, 1);//sets the type to HSV. For some reason 0 is not an option.
transfo->setParameter(4, false);//sets the colorize to false.
transfo->transform(color.data(), color.data() , 1);
}
if (m_colorProperties.useRandomOpacity) {
qreal alpha = drand48();
color.setOpacity(alpha);
m_painter->setOpacity(qRound(alpha * OPACITY_OPAQUE_U8));
}
if (!m_colorProperties.colorPerParticle) {
shouldColor = false;
}
m_painter->setPaintColor(color);
}
// paint some element
switch (m_properties.shape) {
case 0: {
m_painter->paintEllipse(tile);
break;
}
case 1: {
// anti-aliased version
//m_painter->paintRect(tile);
m_dab->fill(tile.topLeft().x(), tile.topLeft().y(), tile.width(), tile.height(), color.data());
break;
}
case 2: {
m_painter->drawDDALine(tile.topRight(), tile.bottomLeft());
break;
}
case 3: {
m_painter->drawLine(tile.topRight(), tile.bottomLeft());
break;
}
case 4: {
m_painter->drawThickLine(tile.topRight(), tile.bottomLeft() , 1, 10);
break;
}
default: {
break;
}
}
if (m_colorProperties.colorPerParticle){
color=painter()->paintColor();//reset color//
}
}
}
QRect rc = m_dab->extent();
painter()->bitBlt(rc.topLeft(), m_dab, rc);
painter()->renderMirrorMask(rc, m_dab);
delete colorPicker;
#ifdef BENCHMARK
int msec = time.elapsed();
- kDebug() << msec << " ms/dab " << "[average: " << m_total / (qreal)m_count << "]";
+ dbgKrita << msec << " ms/dab " << "[average: " << m_total / (qreal)m_count << "]";
m_total += msec;
m_count++;
#endif
return KisSpacingInformation(m_spacing);
}
void KisGridProperties::fillProperties(const KisPropertiesConfiguration* setting)
{
gridWidth = qMax(1, setting->getInt(GRID_WIDTH));
gridHeight = qMax(1, setting->getInt(GRID_HEIGHT));
divisionLevel = qMax(1, setting->getInt(GRID_DIVISION_LEVEL));
pressureDivision = setting->getBool(GRID_PRESSURE_DIVISION);
randomBorder = setting->getBool(GRID_RANDOM_BORDER);
scale = qMax(0.1, setting->getDouble(GRID_SCALE));
vertBorder = setting->getDouble(GRID_VERTICAL_BORDER);
horizBorder = setting->getDouble(GRID_HORIZONTAL_BORDER);
shape = setting->getInt(GRIDSHAPE_SHAPE);
}
diff --git a/krita/plugins/paintops/hairy/hairy_brush.cpp b/krita/plugins/paintops/hairy/hairy_brush.cpp
index 4b924b4a899..c5a0244506a 100644
--- a/krita/plugins/paintops/hairy/hairy_brush.cpp
+++ b/krita/plugins/paintops/hairy/hairy_brush.cpp
@@ -1,467 +1,467 @@
/*
* Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if defined(_WIN32) || defined(_WIN64)
#include <stdlib.h>
#define srand48 srand
inline double drand48()
{
return double(rand()) / RAND_MAX;
}
#endif
#include "hairy_brush.h"
#include "trajectory.h"
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorTransformation.h>
#include <KoCompositeOpRegistry.h>
#include <QVariant>
#include <QHash>
#include <QVector>
#include <kis_types.h>
#include <kis_random_accessor_ng.h>
#include <kis_cross_device_color_picker.h>
#include <kis_fixed_paint_device.h>
#include <cmath>
#include <ctime>
HairyBrush::HairyBrush()
{
srand48(time(0));
m_counter = 0;
m_lastAngle = 0.0;
m_oldPressure = 1.0f;
m_saturationId = -1;
m_transfo = 0;
}
HairyBrush::~HairyBrush()
{
delete m_transfo;
qDeleteAll(m_bristles.begin(), m_bristles.end());
m_bristles.clear();
}
void HairyBrush::initAndCache()
{
m_compositeOp = m_dab->colorSpace()->compositeOp(COMPOSITE_OVER);
m_pixelSize = m_dab->colorSpace()->pixelSize();
if (m_properties->useSaturation) {
m_transfo = m_dab->colorSpace()->createColorTransformation("hsv_adjustment", m_params);
if (m_transfo) {
m_saturationId = m_transfo->parameterId("s");
}
}
}
void HairyBrush::fromDabWithDensity(KisFixedPaintDeviceSP dab, qreal density)
{
int width = dab->bounds().width();
int height = dab->bounds().height();
int centerX = width * 0.5;
int centerY = height * 0.5;
// make mask
Bristle * bristle = 0;
qreal alpha;
quint8 * dabPointer = dab->data();
quint8 pixelSize = dab->pixelSize();
const KoColorSpace * cs = dab->colorSpace();
KoColor bristleColor(cs);
srand48(12345678);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
alpha = cs->opacityF(dabPointer);
if (alpha != 0.0) {
if (density == 1.0 || drand48() <= density) {
memcpy(bristleColor.data(), dabPointer, pixelSize);
bristle = new Bristle(x - centerX, y - centerY, alpha); // using value from image as length of bristle
bristle->setColor(bristleColor);
m_bristles.append(bristle);
}
}
dabPointer += pixelSize;
}
}
}
void HairyBrush::paintLine(KisPaintDeviceSP dab, KisPaintDeviceSP layer, const KisPaintInformation &pi1, const KisPaintInformation &pi2, qreal scale, qreal rotation)
{
m_counter++;
qreal x1 = pi1.pos().x();
qreal y1 = pi1.pos().y();
qreal x2 = pi2.pos().x();
qreal y2 = pi2.pos().y();
qreal dx = x2 - x1;
qreal dy = y2 - y1;
// TODO:this angle is different from the drawing angle in sensor (info.angle()). The bug is caused probably due to
// not computing the drag vector properly in paintBezierLine when smoothing is used
//qreal angle = atan2(dy, dx);
qreal angle = rotation;
qreal mousePressure = 1.0;
if (m_properties->useMousePressure) { // want pressure from mouse movement
qreal distance = sqrt(dx * dx + dy * dy);
mousePressure = (1.0 - computeMousePressure(distance));
scale *= mousePressure;
}
// this pressure controls shear and ink depletion
qreal pressure = mousePressure * (pi2.pressure() * 2);
Bristle *bristle = 0;
KoColor bristleColor(dab->colorSpace());
m_dabAccessor = dab->createRandomAccessorNG((int)x1, (int)y1);
m_dab = dab;
// initialization block
if (firstStroke()) {
initAndCache();
}
// if this is first time the brush touches the canvas and we use soak the ink from canvas
if (firstStroke() && m_properties->useSoakInk) {
if (layer) {
colorifyBristles(layer, pi1.pos());
}
else {
- kWarning() << "Can't soak the ink from the layer";
+ dbgKrita << "Can't soak the ink from the layer";
}
}
qreal fx1, fy1, fx2, fy2;
qreal randomX, randomY;
qreal shear;
float inkDeplation = 0.0;
int inkDepletionSize = m_properties->inkDepletionCurve.size();
int bristleCount = m_bristles.size();
int bristlePathSize;
qreal treshold = 1.0 - pi2.pressure();
for (int i = 0; i < bristleCount; i++) {
if (!m_bristles.at(i)->enabled()) continue;
bristle = m_bristles[i];
randomX = (drand48() * 2 - 1.0) * m_properties->randomFactor;
randomY = (drand48() * 2 - 1.0) * m_properties->randomFactor;
shear = pressure * m_properties->shearFactor;
m_transform.reset();
m_transform.rotateRadians(-angle);
m_transform.scale(scale, scale);
m_transform.translate(randomX, randomY);
m_transform.shear(shear, shear);
if (firstStroke() || (!m_properties->connectedPath)) {
// transform start dab
m_transform.map(bristle->x(), bristle->y(), &fx1, &fy1);
// transform end dab
m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2);
}
else {
// continue the path of the bristle from the previous position
fx1 = bristle->prevX();
fy1 = bristle->prevY();
m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2);
}
// remember the end point
bristle->setPrevX(fx2);
bristle->setPrevY(fy2);
// all coords relative to device position
fx1 += x1;
fy1 += y1;
fx2 += x2;
fy2 += y2;
if (m_properties->threshold && (bristle->length() < treshold)) continue;
// paint between first and last dab
const QVector<QPointF> bristlePath = m_trajectory.getLinearTrajectory(QPointF(fx1, fy1), QPointF(fx2, fy2), 1.0);
bristlePathSize = m_trajectory.size();
memcpy(bristleColor.data(), bristle->color().data() , m_pixelSize);
for (int i = 0; i < bristlePathSize ; i++) {
if (m_properties->inkDepletionEnabled) {
inkDeplation = fetchInkDepletion(bristle, inkDepletionSize);
if (m_properties->useSaturation && m_transfo != 0) {
saturationDepletion(bristle, bristleColor, pressure, inkDeplation);
}
if (m_properties->useOpacity) {
opacityDepletion(bristle, bristleColor, pressure, inkDeplation);
}
}
else {
if (bristleColor.opacityU8() != 0) {
bristleColor.setOpacity(bristle->length());
}
}
addBristleInk(bristle, bristlePath.at(i), bristleColor);
bristle->setInkAmount(1.0 - inkDeplation);
bristle->upIncrement();
}
}
//repositionBristles(angle,slope);
m_dab = 0;
m_dabAccessor = 0;
}
inline qreal HairyBrush::fetchInkDepletion(Bristle* bristle, int inkDepletionSize)
{
if (bristle->counter() >= inkDepletionSize - 1) {
return m_properties->inkDepletionCurve[inkDepletionSize - 1];
} else {
return m_properties->inkDepletionCurve[bristle->counter()];
}
}
void HairyBrush::saturationDepletion(Bristle * bristle, KoColor &bristleColor, qreal pressure, qreal inkDeplation)
{
qreal saturation;
if (m_properties->useWeights) {
// new weighted way (experiment)
saturation = (
(pressure * m_properties->pressureWeight) +
(bristle->length() * m_properties->bristleLengthWeight) +
(bristle->inkAmount() * m_properties->bristleInkAmountWeight) +
((1.0 - inkDeplation) * m_properties->inkDepletionWeight)) - 1.0;
}
else {
// old way of computing saturation
saturation = (
pressure *
bristle->length() *
bristle->inkAmount() *
(1.0 - inkDeplation)) - 1.0;
}
m_transfo->setParameter(m_transfo->parameterId("h"), 0.0);
m_transfo->setParameter(m_transfo->parameterId("v"), 0.0);
m_transfo->setParameter(m_saturationId, saturation);
m_transfo->setParameter(3, 1);//sets the type to
m_transfo->setParameter(4, false);//sets the colorize to none.
m_transfo->transform(bristleColor.data(), bristleColor.data() , 1);
}
void HairyBrush::opacityDepletion(Bristle* bristle, KoColor& bristleColor, qreal pressure, qreal inkDeplation)
{
qreal opacity = OPACITY_OPAQUE_F;
if (m_properties->useWeights) {
opacity = qBound(0.0,
(pressure * m_properties->pressureWeight) +
(bristle->length() * m_properties->bristleLengthWeight) +
(bristle->inkAmount() * m_properties->bristleInkAmountWeight) +
((1.0 - inkDeplation) * m_properties->inkDepletionWeight), 1.0);
}
else {
opacity =
bristle->length() *
bristle->inkAmount();
}
bristleColor.setOpacity(opacity);
}
void HairyBrush::repositionBristles(double angle, double slope)
{
// setX
srand48((int)slope);
for (int i = 0; i < m_bristles.size(); i++) {
float x = m_bristles[i]->x();
m_bristles[i]->setX(x + drand48());
}
// setY
srand48((int)angle);
for (int i = 0; i < m_bristles.size(); i++) {
float y = m_bristles[i]->y();
m_bristles[i]->setY(y + drand48());
}
}
inline void HairyBrush::addBristleInk(Bristle *bristle,const QPointF &pos, const KoColor &color)
{
Q_UNUSED(bristle);
if (m_properties->antialias) {
if (m_properties->useCompositing) {
paintParticle(pos, color);
} else {
paintParticle(pos, color, 1.0);
}
}
else {
int ix = qRound(pos.x());
int iy = qRound(pos.y());
if (m_properties->useCompositing) {
plotPixel(ix, iy, color);
}
else {
darkenPixel(ix, iy, color);
}
}
}
void HairyBrush::paintParticle(QPointF pos, const KoColor& color, qreal weight)
{
// opacity top left, right, bottom left, right
quint8 opacity = color.opacityU8();
opacity *= weight;
int ipx = int (pos.x());
int ipy = int (pos.y());
qreal fx = pos.x() - ipx;
qreal fy = pos.y() - ipy;
quint8 btl = qRound((1.0 - fx) * (1.0 - fy) * opacity);
quint8 btr = qRound((fx) * (1.0 - fy) * opacity);
quint8 bbl = qRound((1.0 - fx) * (fy) * opacity);
quint8 bbr = qRound((fx) * (fy) * opacity);
const KoColorSpace * cs = m_dab->colorSpace();
m_dabAccessor->moveTo(ipx , ipy);
btl = quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, btl + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8));
memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize());
cs->setOpacity(m_dabAccessor->rawData(), btl, 1);
m_dabAccessor->moveTo(ipx + 1, ipy);
btr = quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, btr + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8));
memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize());
cs->setOpacity(m_dabAccessor->rawData(), btr, 1);
m_dabAccessor->moveTo(ipx, ipy + 1);
bbl = quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, bbl + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8));
memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize());
cs->setOpacity(m_dabAccessor->rawData(), bbl, 1);
m_dabAccessor->moveTo(ipx + 1, ipy + 1);
bbr = quint8(qBound<quint16>(OPACITY_TRANSPARENT_U8, bbr + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8));
memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize());
cs->setOpacity(m_dabAccessor->rawData(), bbr, 1);
}
void HairyBrush::paintParticle(QPointF pos, const KoColor& color)
{
// opacity top left, right, bottom left, right
memcpy(m_color.data(), color.data(), m_pixelSize);
quint8 opacity = color.opacityU8();
int ipx = int (pos.x());
int ipy = int (pos.y());
qreal fx = pos.x() - ipx;
qreal fy = pos.y() - ipy;
quint8 btl = qRound((1.0 - fx) * (1.0 - fy) * opacity);
quint8 btr = qRound((fx) * (1.0 - fy) * opacity);
quint8 bbl = qRound((1.0 - fx) * (fy) * opacity);
quint8 bbr = qRound((fx) * (fy) * opacity);
m_color.setOpacity(btl);
plotPixel(ipx , ipy, m_color);
m_color.setOpacity(btr);
plotPixel(ipx + 1 , ipy, m_color);
m_color.setOpacity(bbl);
plotPixel(ipx , ipy + 1, m_color);
m_color.setOpacity(bbr);
plotPixel(ipx + 1 , ipy + 1, m_color);
}
inline void HairyBrush::plotPixel(int wx, int wy, const KoColor &color)
{
m_dabAccessor->moveTo(wx, wy);
m_compositeOp->composite(m_dabAccessor->rawData(), m_pixelSize, color.data() , m_pixelSize, 0, 0, 1, 1, OPACITY_OPAQUE_U8);
}
inline void HairyBrush::darkenPixel(int wx, int wy, const KoColor &color)
{
m_dabAccessor->moveTo(wx, wy);
if (m_dab->colorSpace()->opacityU8(m_dabAccessor->rawData()) < color.opacityU8()) {
memcpy(m_dabAccessor->rawData(), color.data(), m_pixelSize);
}
}
double HairyBrush::computeMousePressure(double distance)
{
static const double scale = 20.0;
static const double minPressure = 0.02;
double oldPressure = m_oldPressure;
double factor = 1.0 - distance / scale;
if (factor < 0.0) factor = 0.0;
double result = ((4.0 * oldPressure) + minPressure + factor) / 5.0;
m_oldPressure = result;
return result;
}
void HairyBrush::colorifyBristles(KisPaintDeviceSP source, QPointF point)
{
KoColor bristleColor(m_dab->colorSpace());
KisCrossDeviceColorPickerInt colorPicker(source, bristleColor);
Bristle *b = 0;
int size = m_bristles.size();
for (int i = 0; i < size; i++) {
b = m_bristles[i];
int x = qRound(b->x() + point.x());
int y = qRound(b->y() + point.y());
colorPicker.pickOldColor(x, y, bristleColor.data());
b->setColor(bristleColor);
}
}
diff --git a/krita/plugins/paintops/hatching/hatching_brush.cpp b/krita/plugins/paintops/hatching/hatching_brush.cpp
index f5b53b6c7db..f3b519fcd21 100644
--- a/krita/plugins/paintops/hatching/hatching_brush.cpp
+++ b/krita/plugins/paintops/hatching/hatching_brush.cpp
@@ -1,300 +1,300 @@
/*
* Copyright (c) 2008,2009,2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "hatching_brush.h"
#include <KoColor.h>
#include <KoColorTransformation.h>
#include <QVariant>
#include <QHash>
#include "kis_random_accessor_ng.h"
#include <cmath>
#include <time.h>
void inline myround(double *x)
{
*x = ((*x - floor(*x)) >= 0.5) ? ceil(*x) : floor(*x);
}
HatchingBrush::HatchingBrush(const KisHatchingPaintOpSettings* settings)
{
m_settings = settings;
// Initializing
separation = m_settings->separation;
origin_x = m_settings->origin_x;
origin_y = m_settings->origin_y;
cursorLineIntercept = 0;
baseLineIntercept = 0;
scanIntercept = 0;
hotIntercept = 0;
slope = 0;
dx = 0;
dy = 0;
}
HatchingBrush::~HatchingBrush()
{
delete m_settings;
}
void HatchingBrush::init()
{
}
void HatchingBrush::hatch(KisPaintDeviceSP dev, qreal x, qreal y, double width, double height, double givenangle, const KoColor &color)
{
m_painter.begin(dev);
m_painter.setFillStyle(KisPainter::FillStyleForegroundColor);
m_painter.setPaintColor(color);
m_painter.setBackgroundColor(color);
angle = givenangle;
double tempthickness = m_settings->thickness * m_settings->thicknesssensorvalue;
if (tempthickness >= 1)
thickness = qRound(tempthickness);
else
thickness = 1;
if (m_settings->enabledcurveseparation)
separation = separationAsFunctionOfParameter(m_settings->separationsensorvalue, m_settings->separation, m_settings->separationintervals);
height_ = height;
width_ = width;
m_painter.setMaskImageSize(width_, height_);
/* dx and dy are the separation between lines in the x and y axis
dx = separation / sin(angle*M_PI/180); csc = 1/sin(angle) */
dy = fabs(separation / cos(angle * M_PI / 180)); // sec = 1/cos(angle)
// I took the absolute value to avoid confusions with negative numbers
if (!m_settings->subpixelprecision)
modf(dy, &dy);
// Exception for vertical lines, for which a tangent does not exist
if ((angle == 90) || (angle == -90)) {
verticalHotX = fmod((origin_x - x), separation);
iterateVerticalLines(true, 1, false); // Forward
iterateVerticalLines(true, 0, true); // In Between both
iterateVerticalLines(false, 1, false); // Backward
}
else {
// Turn Angle + Point into Slope + Intercept
slope = tan(angle * M_PI / 180); // Angle into slope
baseLineIntercept = origin_y - slope * origin_x; // Slope and Point of the Base Line into Intercept
cursorLineIntercept = y - slope * x;
hotIntercept = fmod((baseLineIntercept - cursorLineIntercept), dy); // This hotIntercept belongs to a line that intersects with the hatching area
iterateLines(true, 1, false); // Forward
iterateLines(true, 0, true); // In Between both
iterateLines(false, 1, false); // Backward
// I tried to make this cleaner but there's too many possibilities to be
// worth the micromanagement to optimize
}
}
void HatchingBrush::iterateLines(bool forward, int lineindex, bool oneline)
{
//---Preparations before the loop---
double xdraw[2] = {0, 0};
double ydraw[2] = {0, 0};
//points A and B of the segments to trace
QPointF A, B;
int append_index = 0;
bool remaininginnerlines = true;
while (remaininginnerlines) {
//---------START INTERSECTION POINT VERIFICATION--------
append_index = 0;
remaininginnerlines = false; // We assume there's no more lines unless proven contrary
if (forward)
scanIntercept = hotIntercept + dy * lineindex; // scanIntercept will represent the Intercept of the current line
else
scanIntercept = hotIntercept - dy * lineindex; // scanIntercept will represent the Intercept of the current line
lineindex++; // We are descending vertically out of convenience, see blog entry at pentalis.org/kritablog
/*
Explanation: only 2 out of the 4 segments can include limit values
to verify intersections, otherwise we could encounter a situation where
our new lines intersect with all 4 segments and is still considered an
inner line (for example, a line that goes from corner to corner), thus
triggering an error. The idea is of the algorithm is that only 2 intersections
at most are considered at a time. Graphically this is indistinguishable, it's
just there to avoid making unnecesary control structures (like additional "ifs").
*/
if ((scanIntercept >= 0) && (scanIntercept <= height_)) {
xdraw[append_index] = 0;
ydraw[append_index] = scanIntercept; //interseccion at left
remaininginnerlines = true;
append_index++;
}
if ((slope * width_ + scanIntercept <= height_) && (slope * width_ + scanIntercept >= 0)) {
xdraw[append_index] = width_;
ydraw[append_index] = scanIntercept + slope * width_; //interseccion at right
remaininginnerlines = true;
append_index++;
}
if ((-scanIntercept / slope > 0) && (-scanIntercept / slope < width_)) {
xdraw[append_index] = -scanIntercept / slope;
ydraw[append_index] = 0; //interseccion at top
remaininginnerlines = true;
append_index++;
}
if (((height_ - scanIntercept) / slope > 0) && ((height_ - scanIntercept) / slope < width_)) {
xdraw[append_index] = (height_ - scanIntercept) / slope;
ydraw[append_index] = height_; //interseccion at bottom
remaininginnerlines = true;
append_index++;
}
//--------END INTERSECTION POINT VERIFICATION---------
if (!remaininginnerlines)
break;
if (!m_settings->subpixelprecision) {
myround(&xdraw[0]);
myround(&xdraw[1]);
myround(&ydraw[0]);
myround(&ydraw[1]);
}
A.setX(xdraw[0]);
A.setY(ydraw[0]);
// If 2 lines intersect with the dab square
if (append_index == 2) {
B.setX(xdraw[1]);
B.setY(ydraw[1]);
if (m_settings->antialias)
m_painter.drawThickLine(A, B, thickness, thickness);
else
m_painter.drawLine(A, B, thickness, false); //testing no subpixel;
if (oneline)
break;
}
else {
continue;
/*Drawing points at the vertices causes incosistent results due to
floating point calculations not being quite in sync with algebra,
therefore if I have only 1 intersection (= corner = this case),
don't draw*/
}
}
}
void HatchingBrush::iterateVerticalLines(bool forward, int lineindex, bool oneline)
{
//---Preparations before the loop---
double xdraw = 0;
double ydraw[2] = {0, height_};
//points A and B of the segments to trace
QPointF A, B;
bool remaininginnerlines = true;
while (remaininginnerlines) {
//---------START INTERSECTION POINT VERIFICATION--------
remaininginnerlines = false; // We assume there's no more lines unless proven contrary
if (forward)
verticalScanX = verticalHotX + separation * lineindex;
else
verticalScanX = verticalHotX - separation * lineindex;
lineindex++;
/*Read the explanation in HatchingBrush::iterateLines for more information*/
if ((verticalScanX >= 0) && (verticalScanX <= width_)) {
xdraw = verticalScanX;
remaininginnerlines = true;
}
//--------END INTERSECTION POINT VERIFICATION---------
if (!remaininginnerlines)
break;
if (!m_settings->subpixelprecision) {
myround(&xdraw);
myround(&ydraw[1]);
}
A.setX(xdraw);
A.setY(ydraw[0]);
B.setX(xdraw);
B.setY(ydraw[1]);
if (m_settings->antialias)
m_painter.drawThickLine(A, B, thickness, thickness);
else
m_painter.drawLine(A, B, thickness, false); //testing no subpixel;
if (oneline)
break;
else
continue;
}
}
double HatchingBrush::separationAsFunctionOfParameter(double parameter, double separation, int numintervals)
{
if ((numintervals < 2) || (numintervals > 7)) {
- qDebug() << "Fix your function" << numintervals << "<> 2-7" ;
+ dbgKrita << "Fix your function" << numintervals << "<> 2-7" ;
return separation;
}
double sizeinterval = 1 / double(numintervals);
double lowerlimit = 0;
double upperlimit = 0;
double factor = 0;
int basefactor = numintervals / 2;
// Make the base separation factor tend to greater instead of lesser numbers when numintervals is even
if ((numintervals % 2) == 0)
basefactor--;
for (quint8 currentinterval = 0; currentinterval < numintervals; currentinterval++) {
lowerlimit = upperlimit;
upperlimit += sizeinterval;
if (currentinterval == (numintervals - 1))
upperlimit = 1;
if ((parameter >= lowerlimit) && (parameter <= upperlimit)) {
factor = pow(2.0, (basefactor - currentinterval));
- //qDebug() << factor;
+ //dbgKrita << factor;
return (separation * factor);
}
}
- qDebug() << "Fix your function" << parameter << ">" << upperlimit ;
+ dbgKrita << "Fix your function" << parameter << ">" << upperlimit ;
return separation;
}
diff --git a/krita/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp b/krita/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
index 3dabc508ea0..3e8cd99ca76 100644
--- a/krita/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
+++ b/krita/plugins/paintops/hatching/kis_hatching_paintop_settings.cpp
@@ -1,96 +1,96 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_hatching_paintop_settings.h"
#include <kis_paint_action_type_option.h>
#include <QDomDocument>
#include <QDomElement>
const QString HATCHING_VERSION = "Hatching/Version";
KisHatchingPaintOpSettings::KisHatchingPaintOpSettings()
{
setProperty(HATCHING_VERSION, "2");
}
KisHatchingPaintOpSettings::~KisHatchingPaintOpSettings()
{
}
void KisHatchingPaintOpSettings::initializeTwin(KisHatchingPaintOpSettings* convenienttwin) const
{
/*--------DO NOT REMOVE please, use this to review the XML config tree
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
- qDebug() << i.key() << ":" << i.value();
+ dbgKrita << i.key() << ":" << i.value();
/----------DO NOT REMOVE----------------*/
convenienttwin->enabledcurvecrosshatching = getBool("PressureCrosshatching");
convenienttwin->enabledcurveopacity = getBool("PressureOpacity");
convenienttwin->enabledcurveseparation = getBool("PressureSeparation");
convenienttwin->enabledcurvesize = getBool("PressureSize");
convenienttwin->enabledcurvethickness = getBool("PressureThickness");
convenienttwin->angle = getDouble("Hatching/angle");
convenienttwin->separation = getDouble("Hatching/separation");
convenienttwin->thickness = getDouble("Hatching/thickness");
convenienttwin->origin_x = getDouble("Hatching/origin_x");
convenienttwin->origin_y = getDouble("Hatching/origin_y");
convenienttwin->nocrosshatching = getBool("Hatching/bool_nocrosshatching");
convenienttwin->perpendicular = getBool("Hatching/bool_perpendicular");
convenienttwin->minusthenplus = getBool("Hatching/bool_minusthenplus");
convenienttwin->plusthenminus = getBool("Hatching/bool_plusthenminus");
convenienttwin->moirepattern = getBool("Hatching/bool_moirepattern");
convenienttwin->separationintervals = getInt("Hatching/separationintervals");
//convenienttwin->trigonometryalgebra = getBool("Hatching/bool_trigonometryalgebra");
//convenienttwin->scratchoff = getBool("Hatching/bool_scratchoff");
convenienttwin->antialias = getBool("Hatching/bool_antialias");
convenienttwin->opaquebackground = getBool("Hatching/bool_opaquebackground");
convenienttwin->subpixelprecision = getBool("Hatching/bool_subpixelprecision");
if (getBool("Hatching/bool_nocrosshatching"))
convenienttwin->crosshatchingstyle = 0;
else if (getBool("Hatching/bool_perpendicular"))
convenienttwin->crosshatchingstyle = 1;
else if (getBool("Hatching/bool_minusthenplus"))
convenienttwin->crosshatchingstyle = 2;
else if (getBool("Hatching/bool_plusthenminus"))
convenienttwin->crosshatchingstyle = 3;
if (getBool("Hatching/bool_moirepattern"))
convenienttwin->crosshatchingstyle = 4;
}
void KisHatchingPaintOpSettings::fromXML(const QDomElement& elt)
{
setProperty(HATCHING_VERSION, "1"); // This make sure that fromXML will override HAIRY_VERSION with 2, or will default to 1
KisBrushBasedPaintOpSettings::fromXML(elt);
QVariant v;
if (!getProperty(HATCHING_VERSION, v) || v == "1") {
setProperty("Hatching/thickness", 2.0 * getDouble("Hatching/thickness"));
}
setProperty(HATCHING_VERSION, "2"); // make sure it's saved as version 2 next time
}
diff --git a/krita/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp b/krita/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
index 6ed1fa554fc..ceefb11a762 100644
--- a/krita/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
+++ b/krita/plugins/paintops/hatching/kis_hatching_paintop_settings_widget.cpp
@@ -1,138 +1,138 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_hatching_paintop_settings_widget.h"
#include "kis_hatching_options.h"
#include "kis_hatching_preferences.h"
#include "kis_hatching_paintop_settings.h"
#include "kis_hatching_pressure_crosshatching_option.h"
#include "kis_hatching_pressure_separation_option.h"
#include "kis_hatching_pressure_thickness_option.h"
#include <kis_brush_option_widget.h>
#include <kis_curve_option_widget.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_paintop_settings_widget.h>
#include <kis_paint_action_type_option.h>
#include <kis_compositeop_option.h>
#include "kis_texture_option.h"
#include "kis_curve_option_widget.h"
#include <kis_pressure_mirror_option_widget.h>
#include "kis_pressure_texture_strength_option.h"
#include <QDomDocument>
#include <QDomElement>
KisHatchingPaintOpSettingsWidget:: KisHatchingPaintOpSettingsWidget(QWidget* parent)
: KisBrushBasedPaintopOptionWidget(parent)
{
setPrecisionEnabled(true);
//-------Adding widgets to the screen------------
addPaintOpOption(new KisHatchingOptions(), i18n("Hatching options"));
addPaintOpOption(new KisHatchingPreferences(), i18n("Hatching preferences"));
addPaintOpOption(new KisCompositeOpOption(true), i18n("Blending Mode"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureSeparationOption(), i18n("0.0"), i18n("1.0")), i18n("Separation"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureThicknessOption(), i18n("0.0"), i18n("1.0")), i18n("Thickness"));
addPaintOpOption(new KisCurveOptionWidget(new KisHatchingPressureCrosshatchingOption(), i18n("0.0"), i18n("1.0")), i18n("Crosshatching"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureOpacityOption(), i18n("Transparent"), i18n("Opaque")), i18n("Opacity"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")), i18n("Size"));
addPaintOpOption(new KisPressureMirrorOptionWidget(), i18n("Mirror"));
addPaintOpOption(new KisPaintActionTypeOption(), i18n("Painting Mode"));
addPaintOpOption(new KisTextureOption(), i18n("Pattern"));
addPaintOpOption(new KisCurveOptionWidget(new KisPressureTextureStrengthOption(), i18n("Weak"), i18n("Strong")), i18n("Strength"));
//-----Useful to read first:------
/*
Below you will encounter a reasonably correct solution to the problem of changing
the default presets of the "BrushTip" popup configuration dialogue.
In my (Pentalis) opinion, the best solution is code refactoring (simpler ways
to change the defaults). On the meanwhile, copypasting this code
won't give your class a charisma penalty.
In kis_hatching_paintop_settings.cpp you will find a snippet of code to
discover the structure of your XML config tree if you need to edit it at build
time like here.
*/
//---------START ALTERING DEFAULT VALUES-----------
//As the name implies, reconfigurationCourier is the KisPropertiesConfiguration*
//we'll use as an intermediary to edit the default settings
KisPropertiesConfiguration* reconfigurationCourier = configuration();
/*xMLAnalyzer is an empty document we'll use to analyze and edit the config string part by part
I know the important string is "brush_definition" because I read the tree with the snippet
in kis_hatching_paintop_settings.cpp */
QDomDocument xMLAnalyzer("");
xMLAnalyzer.setContent(reconfigurationCourier->getString("brush_definition"));
/*More things I know by reading the XML tree. At this point you can just read it with:
- qDebug() << xMLAnalyzer.toString() ;
+ dbgKrita << xMLAnalyzer.toString() ;
those QDomElements are the way to navigate the XML tree, read
http://doc.qt.nokia.com/latest/qdomdocument.html for more information */
QDomElement firstTag = xMLAnalyzer.documentElement();
QDomElement firstTagsChild = firstTag.elementsByTagName("MaskGenerator").item(0).toElement();
// SET THE DEFAULT VALUES
firstTag.attributeNode("spacing").setValue("0.4");
firstTagsChild.attributeNode("diameter").setValue("30");
//Write them into the intermediary config file
reconfigurationCourier->setProperty("brush_definition", xMLAnalyzer.toString());
KisCubicCurve CurveSize;
CurveSize.fromString("0,1;1,0.1;");
- //qDebug() << "\n\n\n" << CurveSize.toString() << "\n\n\n";
+ //dbgKrita << "\n\n\n" << CurveSize.toString() << "\n\n\n";
QVariant QVCurveSize = QVariant::fromValue(CurveSize);
reconfigurationCourier->setProperty("CurveSize", QVCurveSize);
setConfiguration(reconfigurationCourier); // Finished.
/* Debugging block
QMap<QString, QVariant> rofl = QMap<QString, QVariant>(reconfigurationCourier->getProperties());
QMap<QString, QVariant>::const_iterator i;
for (i = rofl.constBegin(); i != rofl.constEnd(); ++i)
- qDebug() << i.key() << ":" << i.value();
+ dbgKrita << i.key() << ":" << i.value();
*/
delete reconfigurationCourier;
}
KisHatchingPaintOpSettingsWidget::~ KisHatchingPaintOpSettingsWidget()
{
}
KisPropertiesConfiguration* KisHatchingPaintOpSettingsWidget::configuration() const
{
KisHatchingPaintOpSettings* config = new KisHatchingPaintOpSettings();
config->setOptionsWidget(const_cast<KisHatchingPaintOpSettingsWidget*>(this));
config->setProperty("paintop", "hatchingbrush"); // XXX: make this a const id string
writeConfiguration(config);
return config;
}
diff --git a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
index fe1f24d98bf..179880570ca 100644
--- a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop_settings.cpp
@@ -1,122 +1,122 @@
/*
* Copyright (c) 2010 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_brush_based_paintop_settings.h"
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option.h>
#include "kis_brush_based_paintop_options_widget.h"
#include <kis_boundary.h>
#include "kis_brush_server.h"
#include <QLineF>
KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings()
: KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
KisCurrentOutlineFetcher::ROTATION_OPTION |
KisCurrentOutlineFetcher::MIRROR_OPTION)
{
}
bool KisBrushBasedPaintOpSettings::paintIncremental()
{
if (hasProperty("PaintOpAction")) {
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
return true;
}
bool KisBrushBasedPaintOpSettings::isAirbrushing() const
{
return getBool(AIRBRUSH_ENABLED);
}
int KisBrushBasedPaintOpSettings::rate() const
{
return getInt(AIRBRUSH_RATE);
}
QPainterPath KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info,
OutlineMode mode,
qreal additionalScale,
bool forceOutline) const
{
QPainterPath path;
if (forceOutline || mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) {
KisBrushBasedPaintopOptionWidget *widget = dynamic_cast<KisBrushBasedPaintopOptionWidget*>(optionsWidget());
if (!widget) {
return KisPaintOpSettings::brushOutline(info, mode);
}
KisBrushSP brush = widget->brush();
qreal finalScale = brush->scale() * additionalScale;
QPainterPath realOutline = brush->outline();
QPainterPath tiltLine;
QLineF tiltAngle(realOutline.boundingRect().center(), realOutline.boundingRect().topLeft());
- tiltAngle.setLength(qMax(realOutline.boundingRect().width()*0.5, qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
+ tiltAngle.setLength(qMax(realOutline.boundingRect().width()*qreal(0.5), qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0);
tiltLine.moveTo(tiltAngle.p1());
tiltLine.lineTo(tiltAngle.p2());
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0);
tiltLine.lineTo(tiltAngle.p2());
tiltLine.lineTo(tiltAngle.p1());
if (mode == CursorIsCircleOutline || mode == CursorTiltOutline ||
(forceOutline && mode == CursorNoOutline)) {
QPainterPath ellipse;
ellipse.addEllipse(realOutline.boundingRect());
realOutline = ellipse;
}
path = outlineFetcher()->fetchOutline(info, this, realOutline, finalScale, brush->angle());
if (mode == CursorTiltOutline) {
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y()));
}
}
return path;
}
QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
return brushOutlineImpl(info, mode, 1.0);
}
bool KisBrushBasedPaintOpSettings::isValid() const
{
QString filename = getString("requiredBrushFile", "");
if (!filename.isEmpty()) {
KisBrushSP brush = KisBrushServer::instance()->brushServer()->resourceByFilename(filename);
if (!brush) {
return false;
}
}
return true;
}
bool KisBrushBasedPaintOpSettings::isLoadable()
{
return (KisBrushServer::instance()->brushServer()->resources().count() > 0);
}
diff --git a/krita/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp b/krita/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp
index ebd647728ee..38fb879ba65 100644
--- a/krita/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_current_outline_fetcher.cpp
@@ -1,158 +1,160 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_current_outline_fetcher.h"
#include "kis_pressure_size_option.h"
#include "kis_pressure_rotation_option.h"
#include "kis_pressure_mirror_option.h"
#include "kis_paintop_settings.h"
+#include <QElapsedTimer>
+
#define NOISY_UPDATE_SPEED 50 // Time in ms for outline updates to noisy brushes
struct KisCurrentOutlineFetcher::Private {
Private(Options optionsAvailable)
: isDirty(true) {
if (optionsAvailable & SIZE_OPTION) {
sizeOption.reset(new KisPressureSizeOption());
}
if (optionsAvailable & ROTATION_OPTION) {
rotationOption.reset(new KisPressureRotationOption());
}
if (optionsAvailable & MIRROR_OPTION) {
mirrorOption.reset(new KisPressureMirrorOption());
}
}
QScopedPointer<KisPressureSizeOption> sizeOption;
QScopedPointer<KisPressureRotationOption> rotationOption;
QScopedPointer<KisPressureMirrorOption> mirrorOption;
bool isDirty;
QElapsedTimer lastUpdateTime;
qreal lastRotationApplied;
qreal lastSizeApplied;
MirrorProperties lastMirrorApplied;
};
KisCurrentOutlineFetcher::KisCurrentOutlineFetcher(Options optionsAvailable)
: d(new Private(optionsAvailable))
{
d->lastUpdateTime.start();
}
KisCurrentOutlineFetcher::~KisCurrentOutlineFetcher()
{
}
void KisCurrentOutlineFetcher::setDirty()
{
d->isDirty = true;
}
QPainterPath KisCurrentOutlineFetcher::fetchOutline(const KisPaintInformation &info,
const KisPaintOpSettings *settings,
const QPainterPath &originalOutline,
qreal additionalScale,
qreal additionalRotation,
bool tilt, qreal tiltcenterx, qreal tiltcentery) const
{
if (d->isDirty) {
if (d->sizeOption) {
d->sizeOption->readOptionSetting(settings);
d->sizeOption->resetAllSensors();
}
if (d->rotationOption) {
d->rotationOption->readOptionSetting(settings);
d->rotationOption->resetAllSensors();
}
if (d->mirrorOption) {
d->mirrorOption->readOptionSetting(settings);
d->mirrorOption->resetAllSensors();
}
d->isDirty = false;
}
qreal scale = additionalScale;
qreal rotation = additionalRotation;
MirrorProperties mirrorProperties;
bool needsUpdate = false;
// Randomized rotation at full speed looks noisy, so slow it down
if (d->lastUpdateTime.elapsed() > NOISY_UPDATE_SPEED) {
needsUpdate = true;
d->lastUpdateTime.restart();
}
if (d->sizeOption && tilt == false) {
if (!d->sizeOption->isRandom() || needsUpdate) {
d->lastSizeApplied = d->sizeOption->apply(info);
}
scale *= d->lastSizeApplied;
}
if (d->rotationOption && tilt == false) {
if (!d->rotationOption->isRandom() || needsUpdate) {
d->lastRotationApplied = d->rotationOption->apply(info);
}
rotation += d->lastRotationApplied;
} else if (d->rotationOption && tilt == true) {
rotation += settings->getDouble("runtimeCanvasRotation", 0.0) * M_PI / 180.0;
}
qreal xFlip = 1.0;
qreal yFlip = 1.0;
if (d->mirrorOption) {
if (!d->mirrorOption->isRandom() || needsUpdate) {
d->lastMirrorApplied = d->mirrorOption->apply(info);
}
if (d->lastMirrorApplied.coordinateSystemFlipped) {
rotation = 2 * M_PI - rotation;
}
if (d->lastMirrorApplied.horizontalMirror) {
xFlip = -1.0;
}
if (d->lastMirrorApplied.verticalMirror) {
yFlip = -1.0;
}
}
QTransform rot;
rot.rotateRadians(-rotation);
QPointF hotSpot = originalOutline.boundingRect().center();
if (tilt==true) {
hotSpot.setX(tiltcenterx);hotSpot.setY(tiltcentery);
}
QTransform T1 = QTransform::fromTranslate(-hotSpot.x(), -hotSpot.y());
QTransform T2 = QTransform::fromTranslate(info.pos().x(), info.pos().y());
QTransform S = QTransform::fromScale(xFlip * scale, yFlip * scale);
return (T1 * rot * S * T2).map(originalOutline);
}
diff --git a/krita/plugins/paintops/libpaintop/kis_curve_option.cpp b/krita/plugins/paintops/libpaintop/kis_curve_option.cpp
index 8814ba48e59..36cddbb27f1 100644
--- a/krita/plugins/paintops/libpaintop/kis_curve_option.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_curve_option.cpp
@@ -1,366 +1,366 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_curve_option.h"
#include <QDomNode>
KisCurveOption::KisCurveOption(const QString& name, KisPaintOpOption::PaintopCategory category,
bool checked, qreal value, qreal min, qreal max)
: m_name(name)
, m_category(category)
, m_checkable(true)
, m_checked(checked)
, m_useCurve(true)
, m_useSameCurve(true)
, m_separateCurveValue(false)
{
foreach (const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) {
KisDynamicSensorSP sensor = KisDynamicSensor::type2Sensor(sensorType);
sensor->setActive(false);
replaceSensor(sensor);
}
m_sensorMap[PRESSURE]->setActive(true);
setValueRange(min, max);
setValue(value);
}
KisCurveOption::~KisCurveOption()
{
m_sensorMap.clear();
}
const QString& KisCurveOption::name() const
{
return m_name;
}
KisPaintOpOption::PaintopCategory KisCurveOption::category() const
{
return m_category;
}
qreal KisCurveOption::minValue() const
{
return m_minValue;
}
qreal KisCurveOption::maxValue() const
{
return m_maxValue;
}
qreal KisCurveOption::value() const
{
return m_value;
}
void KisCurveOption::resetAllSensors()
{
foreach (KisDynamicSensorSP sensor, m_sensorMap.values()) {
if (sensor->isActive()) {
sensor->reset();
}
}
}
void KisCurveOption::writeOptionSetting(KisPropertiesConfiguration* setting) const
{
if (m_checkable) {
setting->setProperty("Pressure" + m_name, isChecked());
}
if (activeSensors().size() == 1) {
setting->setProperty(m_name + "Sensor", activeSensors().first()->toXML());
}
else {
QDomDocument doc = QDomDocument("params");
QDomElement root = doc.createElement("params");
doc.appendChild(root);
root.setAttribute("id", "sensorslist");
foreach (KisDynamicSensorSP sensor, activeSensors()) {
QDomElement childelt = doc.createElement("ChildSensor");
sensor->toXML(doc, childelt);
root.appendChild(childelt);
}
setting->setProperty(m_name + "Sensor", doc.toString());
}
setting->setProperty(m_name + "UseCurve", m_useCurve);
setting->setProperty(m_name + "UseSameCurve", m_useSameCurve);
setting->setProperty(m_name + "Value", m_value);
}
void KisCurveOption::readOptionSetting(const KisPropertiesConfiguration* setting)
{
m_curveCache.clear();
readNamedOptionSetting(m_name, setting);
}
void KisCurveOption::readNamedOptionSetting(const QString& prefix, const KisPropertiesConfiguration* setting)
{
if (!setting) return;
- //qDebug() << "readNamedOptionSetting" << prefix;
+ //dbgKrita << "readNamedOptionSetting" << prefix;
setting->dump();
if (m_checkable) {
setChecked(setting->getBool("Pressure" + prefix, false));
}
- //qDebug() << "\tPressure" + prefix << isChecked();
+ //dbgKrita << "\tPressure" + prefix << isChecked();
m_sensorMap.clear();
// Replace all sensors with the inactive defaults
foreach(const DynamicSensorType sensorType, KisDynamicSensor::sensorsTypes()) {
replaceSensor(KisDynamicSensor::type2Sensor(sensorType));
}
QString sensorDefinition = setting->getString(prefix + "Sensor");
if (!sensorDefinition.contains("sensorslist")) {
KisDynamicSensorSP s = KisDynamicSensor::createFromXML(sensorDefinition);
if (s) {
replaceSensor(s);
s->setActive(true);
- //qDebug() << "\tsingle sensor" << s::id(s->sensorType()) << s->isActive() << "added";
+ //dbgKrita << "\tsingle sensor" << s::id(s->sensorType()) << s->isActive() << "added";
}
}
else {
QDomDocument doc;
doc.setContent(sensorDefinition);
QDomElement elt = doc.documentElement();
QDomNode node = elt.firstChild();
while (!node.isNull()) {
if (node.isElement()) {
QDomElement childelt = node.toElement();
if (childelt.tagName() == "ChildSensor") {
KisDynamicSensorSP s = KisDynamicSensor::createFromXML(childelt);
if (s) {
replaceSensor(s);
s->setActive(true);
- //qDebug() << "\tchild sensor" << s::id(s->sensorType()) << s->isActive() << "added";
+ //dbgKrita << "\tchild sensor" << s::id(s->sensorType()) << s->isActive() << "added";
}
}
}
node = node.nextSibling();
}
}
// Only load the old curve format if the curve wasn't saved by the sensor
// This will give every sensor the same curve.
- //qDebug() << ">>>>>>>>>>>" << prefix + "Sensor" << setting->getString(prefix + "Sensor");
+ //dbgKrita << ">>>>>>>>>>>" << prefix + "Sensor" << setting->getString(prefix + "Sensor");
if (!setting->getString(prefix + "Sensor").contains("curve")) {
- //qDebug() << "\told format";
+ //dbgKrita << "\told format";
if (setting->getBool("Custom" + prefix, false)) {
foreach(KisDynamicSensorSP s, m_sensorMap.values()) {
s->setCurve(setting->getCubicCurve("Curve" + prefix));
}
}
}
// At least one sensor needs to be active
if (activeSensors().size() == 0) {
m_sensorMap[PRESSURE]->setActive(true);
}
m_value = setting->getDouble(m_name + "Value", m_maxValue);
- //qDebug() << "\t" + m_name + "Value" << m_value;
+ //dbgKrita << "\t" + m_name + "Value" << m_value;
m_useCurve = setting->getBool(m_name + "UseCurve", true);
- //qDebug() << "\t" + m_name + "UseCurve" << m_useSameCurve;
+ //dbgKrita << "\t" + m_name + "UseCurve" << m_useSameCurve;
m_useSameCurve = setting->getBool(m_name + "UseSameCurve", true);
- //qDebug() << "\t" + m_name + "UseSameCurve" << m_useSameCurve;
+ //dbgKrita << "\t" + m_name + "UseSameCurve" << m_useSameCurve;
- //qDebug() << "-----------------";
+ //dbgKrita << "-----------------";
}
void KisCurveOption::replaceSensor(KisDynamicSensorSP s)
{
Q_ASSERT(s);
m_sensorMap[s->sensorType()] = s;
}
KisDynamicSensorSP KisCurveOption::sensor(DynamicSensorType sensorType, bool active) const
{
if (m_sensorMap.contains(sensorType)) {
if (!active) {
return m_sensorMap[sensorType];
}
else {
if (m_sensorMap[sensorType]->isActive()) {
return m_sensorMap[sensorType];
}
}
}
return 0;
}
bool KisCurveOption::isRandom() const
{
return (bool) sensor(FUZZY, true);
}
bool KisCurveOption::isCurveUsed() const
{
return m_useCurve;
}
bool KisCurveOption::isSameCurveUsed() const
{
return m_useSameCurve;
}
void KisCurveOption::setSeparateCurveValue(bool separateCurveValue)
{
m_separateCurveValue = separateCurveValue;
}
bool KisCurveOption::isCheckable()
{
return m_checkable;
}
bool KisCurveOption::isChecked() const
{
return m_checked;
}
void KisCurveOption::setChecked(bool checked)
{
m_checked = checked;
}
void KisCurveOption::setCurveUsed(bool useCurve)
{
m_useCurve = useCurve;
}
void KisCurveOption::setCurve(DynamicSensorType sensorType, bool useSameCurve, const KisCubicCurve &curve)
{
// No switch in state, don't mess with the cache
if (useSameCurve == m_useSameCurve) {
if (useSameCurve) {
foreach(KisDynamicSensorSP s, m_sensorMap.values()) {
s->setCurve(curve);
}
}
else {
KisDynamicSensorSP s = sensor(sensorType, false);
if (s) {
s->setCurve(curve);
}
}
}
else {
// moving from not use same curve to use same curve: backup the custom curves
if (!m_useSameCurve && useSameCurve) {
// Copy the custom curves to the cache and set the new curve on all sensors, active or not
m_curveCache.clear();
foreach(KisDynamicSensorSP s, m_sensorMap.values()) {
m_curveCache[s->sensorType()] = s->curve();
s->setCurve(curve);
}
}
else { //if (m_useSameCurve && !useSameCurve)
// Restore the cached curves
KisDynamicSensorSP s = 0;
foreach(DynamicSensorType sensorType, m_curveCache.keys()) {
if (m_sensorMap.contains(sensorType)) {
s = m_sensorMap[sensorType];
}
else {
s = KisDynamicSensor::type2Sensor(sensorType);
}
s->setCurve(m_curveCache[sensorType]);
m_sensorMap[sensorType] = s;
}
s = 0;
// And set the current sensor to the current curve
if (!m_sensorMap.contains(sensorType)) {
s = KisDynamicSensor::type2Sensor(sensorType);
}
if (s) {
s->setCurve(curve);
s->setCurve(m_curveCache[sensorType]);
}
}
m_useSameCurve = useSameCurve;
}
}
void KisCurveOption::setValueRange(qreal min, qreal max)
{
m_minValue = qMin(min, max);
m_maxValue = qMax(min, max);
}
void KisCurveOption::setValue(qreal value)
{
m_value = qBound(m_minValue, value, m_maxValue);
}
double KisCurveOption::computeValue(const KisPaintInformation& info) const
{
if (!m_useCurve) {
if (m_separateCurveValue) {
return 1.0;
}
else {
return m_value;
}
}
else {
qreal t = 1.0;
foreach (KisDynamicSensorSP s, m_sensorMap.values()) {
- ////qDebug() << "\tTesting" << s->name() << s->isActive();
+ ////dbgKrita << "\tTesting" << s->name() << s->isActive();
if (s->isActive()) {
t *= s->parameter(info);
}
}
if (m_separateCurveValue) {
return t;
}
else {
return m_minValue + (m_value - m_minValue) * t;
}
}
}
QList<KisDynamicSensorSP> KisCurveOption::sensors()
{
- //qDebug() << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
+ //dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
return m_sensorMap.values();
}
QList<KisDynamicSensorSP> KisCurveOption::activeSensors() const
{
QList<KisDynamicSensorSP> sensorList;
foreach(KisDynamicSensorSP sensor, m_sensorMap.values()) {
if (sensor->isActive()) {
sensorList << sensor;
}
}
- //qDebug() << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
+ //dbgKrita << "ID" << name() << "has" << m_sensorMap.count() << "Sensors of which" << sensorList.count() << "are active.";
return sensorList;
}
diff --git a/krita/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp b/krita/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp
index b291309de1a..f7cd7237e2c 100644
--- a/krita/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_multi_sensors_model_p.cpp
@@ -1,125 +1,125 @@
/*
* Copyright (c) 2011 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_multi_sensors_model_p.h"
#include "kis_dynamic_sensor.h"
#include "kis_curve_option.h"
KisMultiSensorsModel::KisMultiSensorsModel(QObject* parent)
: QAbstractListModel(parent)
, m_curveOption(0)
{
}
KisMultiSensorsModel::~KisMultiSensorsModel()
{
}
void KisMultiSensorsModel::setCurveOption(KisCurveOption *curveOption)
{
beginResetModel();
m_curveOption = curveOption;
endResetModel();
}
int KisMultiSensorsModel::rowCount(const QModelIndex &/*parent*/) const
{
if (m_curveOption) {
return m_curveOption->sensors().size();
}
else {
return 0;
}
}
QVariant KisMultiSensorsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) return QVariant();
if (role == Qt::DisplayRole) {
return KisDynamicSensor::sensorsIds()[index.row()].name();
}
else if (role == Qt::CheckStateRole) {
QString selectedSensorId = KisDynamicSensor::sensorsIds()[index.row()].id();
KisDynamicSensorSP sensor = m_curveOption->sensor(KisDynamicSensor::id2Type(selectedSensorId), false);
if (sensor) {
- //qDebug() << sensor->id() << sensor->isActive();
+ //dbgKrita << sensor->id() << sensor->isActive();
return QVariant(sensor->isActive() ? Qt::Checked : Qt::Unchecked);
}
else {
return QVariant(Qt::Unchecked);
}
}
return QVariant();
}
bool KisMultiSensorsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool result = false;
if (role == Qt::CheckStateRole) {
bool checked = (value.toInt() == Qt::Checked);
if (checked || m_curveOption->activeSensors().size() != 1) { // Don't uncheck the last sensor (but why not?)
KisDynamicSensorSP sensor = m_curveOption->sensor(KisDynamicSensor::id2Type(KisDynamicSensor::sensorsIds()[index.row()].id()), false);
if (!sensor) {
sensor = KisDynamicSensor::id2Sensor(KisDynamicSensor::sensorsIds()[index.row()].id());
m_curveOption->replaceSensor(sensor);
}
sensor->setActive(checked);
emit(parametersChanged());
result = true;
}
}
return result;
}
Qt::ItemFlags KisMultiSensorsModel::flags(const QModelIndex & /*index */) const
{
return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
}
KisDynamicSensorSP KisMultiSensorsModel::getSensor(const QModelIndex& index)
{
if (!index.isValid()) return 0;
QString id = KisDynamicSensor::sensorsIds()[index.row()].id();
return m_curveOption->sensor(KisDynamicSensor::id2Type(id), false);
}
void KisMultiSensorsModel::setCurrentCurve(const QModelIndex& currentIndex, const KisCubicCurve& curve, bool useSameCurve)
{
if (!currentIndex.isValid()) return;
QString selectedSensorId = KisDynamicSensor::sensorsIds()[currentIndex.row()].id();
m_curveOption->setCurve(KisDynamicSensor::id2Type(selectedSensorId), useSameCurve, curve);
}
QModelIndex KisMultiSensorsModel::sensorIndex(KisDynamicSensorSP arg1)
{
return index(KisDynamicSensor::sensorsIds().indexOf(KoID(KisDynamicSensor::id(arg1->sensorType()))));
}
void KisMultiSensorsModel::resetCurveOption()
{
beginResetModel();
reset();
endResetModel();
}
diff --git a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp
index ce61484a8dc..762b407caa2 100644
--- a/krita/plugins/paintops/libpaintop/kis_texture_option.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_texture_option.cpp
@@ -1,394 +1,394 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2012
* Copyright (C) Mohit Goyal <mohit.bits2011@gmail.com>, (C) 2014
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_texture_option.h"
#include <QWidget>
#include <QString>
#include <QByteArray>
#include <QCheckBox>
#include <QBuffer>
#include <QFormLayout>
#include <QLabel>
#include <QComboBox>
#include <QTransform>
#include <QPainter>
#include <QBoxLayout>
#include <klocale.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_resource_server_provider.h>
#include <kis_pattern_chooser.h>
#include <kis_slider_spin_box.h>
#include <kis_multipliers_double_slider_spinbox.h>
#include <KoPattern.h>
#include <kis_paint_device.h>
#include <kis_fill_painter.h>
#include <kis_painter.h>
#include <kis_iterator_ng.h>
#include <kis_fixed_paint_device.h>
#include <kis_gradient_slider.h>
#include "kis_embedded_pattern_manager.h"
#include "kis_algebra_2d.h"
#include <time.h>
class KisTextureOptionWidget : public QWidget
{
public:
KisTextureOptionWidget(QWidget *parent = 0)
: QWidget(parent) {
QFormLayout *formLayout = new QFormLayout(this);
formLayout->setMargin(0);
chooser = new KisPatternChooser(this);
chooser->setGrayscalePreview(true);
chooser->setMaximumHeight(250);
chooser->setCurrentItem(0, 0);
formLayout->addRow(chooser);
scaleSlider = new KisMultipliersDoubleSliderSpinBox(this);
scaleSlider->setRange(0.0, 2.0, 2);
scaleSlider->setValue(1.0);
scaleSlider->addMultiplier(0.1);
scaleSlider->addMultiplier(2);
scaleSlider->addMultiplier(10);
formLayout->addRow(i18n("Scale:"), scaleSlider);
QBoxLayout *offsetLayoutX = new QBoxLayout(QBoxLayout::LeftToRight);
offsetSliderX = new KisSliderSpinBox(this);
offsetSliderX->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
randomOffsetX = new QCheckBox(i18n("Random Offset"),this);
offsetLayoutX->addWidget(offsetSliderX,1,0);
offsetLayoutX->addWidget(randomOffsetX,0,0);
formLayout->addRow(i18n("Horizontal Offset:"), offsetLayoutX);
QBoxLayout *offsetLayoutY = new QBoxLayout(QBoxLayout::LeftToRight);
offsetSliderY = new KisSliderSpinBox(this);
offsetSliderY->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
randomOffsetY = new QCheckBox(i18n("Random Offset"),this);
offsetLayoutY->addWidget(offsetSliderY,1,0);
offsetLayoutY->addWidget(randomOffsetY,0,0);
formLayout->addRow(i18n("Vertical Offset:"), offsetLayoutY);
cmbTexturingMode = new QComboBox(this);
cmbTexturingMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QStringList texturingModes;
texturingModes << i18n("Multiply") << i18n("Subtract");
cmbTexturingMode->addItems(texturingModes);
formLayout->addRow(i18n("Texturing Mode:"), cmbTexturingMode);
cmbTexturingMode->setCurrentIndex(KisTextureProperties::SUBTRACT);
cmbCutoffPolicy = new QComboBox(this);
cmbCutoffPolicy->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QStringList cutOffPolicies;
cutOffPolicies << i18n("Cut Off Disabled") << i18n("Cut Off Brush") << i18n("Cut Off Pattern");
cmbCutoffPolicy->addItems(cutOffPolicies);
formLayout->addRow(i18n("Cutoff Policy:"), cmbCutoffPolicy);
cutoffSlider = new KisGradientSlider(this);
cutoffSlider->setMinimumSize(256, 30);
cutoffSlider->enableGamma(false);
cutoffSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified"
" by the slider, the cut-off policy will be applied."));
formLayout->addRow(i18n("Cutoff:"), cutoffSlider);
chkInvert = new QCheckBox(this);
chkInvert->setChecked(false);
formLayout->addRow(i18n("Invert Pattern:"), chkInvert);
setLayout(formLayout);
}
KisPatternChooser *chooser;
KisMultipliersDoubleSliderSpinBox *scaleSlider;
KisSliderSpinBox *offsetSliderX;
QCheckBox *randomOffsetX;
KisSliderSpinBox *offsetSliderY;
QCheckBox *randomOffsetY;
QComboBox *cmbTexturingMode;
KisGradientSlider *cutoffSlider;
QComboBox *cmbCutoffPolicy;
QCheckBox *chkInvert;
};
KisTextureOption::KisTextureOption(QObject *)
: KisPaintOpOption(KisPaintOpOption::TEXTURE, true)
{
setObjectName("KisTextureOption");
setChecked(false);
m_optionWidget = new KisTextureOptionWidget;
m_optionWidget->hide();
setConfigurationPage(m_optionWidget);
connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*)));
connect(m_optionWidget->chooser, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged()));
connect(m_optionWidget->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_optionWidget->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged()));
connect(m_optionWidget->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
resetGUI(m_optionWidget->chooser->currentResource());
}
KisTextureOption::~KisTextureOption()
{
delete m_optionWidget;
}
void KisTextureOption::writeOptionSetting(KisPropertiesConfiguration* setting) const
{
m_optionWidget->chooser->blockSignals(true); // Checking
if (!m_optionWidget->chooser->currentResource()) return;
KoPattern *pattern = static_cast<KoPattern*>(m_optionWidget->chooser->currentResource());
m_optionWidget->chooser->blockSignals(false); // Checking
if (!pattern) return;
setting->setProperty("Texture/Pattern/Enabled", isChecked());
if (!isChecked()) {
return;
}
qreal scale = m_optionWidget->scaleSlider->value();
int offsetX = m_optionWidget->offsetSliderX->value();
if (m_optionWidget ->randomOffsetX->isChecked()) {
m_optionWidget -> offsetSliderX ->setEnabled(false);
m_optionWidget -> offsetSliderX ->blockSignals(true);
m_optionWidget -> offsetSliderX ->setValue(offsetX);
m_optionWidget -> offsetSliderX ->blockSignals(false);
srand(time(0));
}
else {
m_optionWidget -> offsetSliderX ->setEnabled(true);
}
int offsetY = m_optionWidget->offsetSliderY->value();
if (m_optionWidget ->randomOffsetY->isChecked()) {
m_optionWidget -> offsetSliderY ->setEnabled(false);
m_optionWidget -> offsetSliderY ->blockSignals(true);
m_optionWidget -> offsetSliderY ->setValue(offsetY);
m_optionWidget -> offsetSliderY ->blockSignals(false);
srand(time(0));
}
else {
m_optionWidget -> offsetSliderY ->setEnabled(true);
}
int texturingMode = m_optionWidget->cmbTexturingMode->currentIndex();
bool invert = (m_optionWidget->chkInvert->checkState() == Qt::Checked);
setting->setProperty("Texture/Pattern/Scale", scale);
setting->setProperty("Texture/Pattern/OffsetX", offsetX);
setting->setProperty("Texture/Pattern/OffsetY", offsetY);
setting->setProperty("Texture/Pattern/TexturingMode", texturingMode);
setting->setProperty("Texture/Pattern/CutoffLeft", m_optionWidget->cutoffSlider->black());
setting->setProperty("Texture/Pattern/CutoffRight", m_optionWidget->cutoffSlider->white());
setting->setProperty("Texture/Pattern/CutoffPolicy", m_optionWidget->cmbCutoffPolicy->currentIndex());
setting->setProperty("Texture/Pattern/Invert", invert);
setting->setProperty("Texture/Pattern/MaximumOffsetX",m_optionWidget -> offsetSliderX ->maximum());
setting->setProperty("Texture/Pattern/MaximumOffsetY",m_optionWidget -> offsetSliderY ->maximum());
setting->setProperty("Texture/Pattern/isRandomOffsetX",m_optionWidget ->randomOffsetX ->isChecked());
setting->setProperty("Texture/Pattern/isRandomOffsetY",m_optionWidget ->randomOffsetY ->isChecked());
KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern);
}
void KisTextureOption::readOptionSetting(const KisPropertiesConfiguration* setting)
{
setChecked(setting->getBool("Texture/Pattern/Enabled"));
if (!isChecked()) {
return;
}
KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
if (!pattern) {
pattern = static_cast<KoPattern*>(m_optionWidget->chooser->currentResource());
}
m_optionWidget->chooser->setCurrentPattern(pattern);
m_optionWidget->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0));
m_optionWidget->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX"));
m_optionWidget->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY"));
m_optionWidget->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY));
m_optionWidget->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy"));
m_optionWidget->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0));
m_optionWidget->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255));
m_optionWidget->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert"));
}
void KisTextureOption::resetGUI(KoResource* res)
{
KoPattern *pattern = static_cast<KoPattern *>(res);
if (!pattern) return;
m_optionWidget->offsetSliderX->setRange(0, pattern->pattern().width() / 2);
m_optionWidget->offsetSliderY->setRange(0, pattern->pattern().height() / 2);
}
void KisTextureProperties::recalculateMask()
{
if (!m_pattern) return;
m_mask = 0;
QImage mask = m_pattern->pattern();
if (mask.format() != QImage::Format_RGB32 ||
mask.format() != QImage::Format_ARGB32) {
mask = mask.convertToFormat(QImage::Format_ARGB32);
}
if (!qFuzzyCompare(m_scale, 0.0)) {
QTransform tf;
tf.scale(m_scale, m_scale);
QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2));
mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
const QRgb* pixel = reinterpret_cast<const QRgb*>(mask.constBits());
int width = mask.width();
int height = mask.height();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
m_mask = new KisPaintDevice(cs);
KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width);
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
const QRgb currentPixel = pixel[row * width + col];
const int red = qRed(currentPixel);
const int green = qGreen(currentPixel);
const int blue = qBlue(currentPixel);
float alpha = qAlpha(currentPixel) / 255.0;
const int grayValue = (red * 11 + green * 16 + blue * 5) / 32;
float maskValue = (grayValue / 255.0) * alpha + (1 - alpha);
if (m_invert) {
maskValue = 1 - maskValue;
}
if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) {
// mask out the dab if it's outside the pattern's cuttoff points
maskValue = OPACITY_TRANSPARENT_F;
}
else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) {
maskValue = OPACITY_OPAQUE_F;
}
cs->setOpacity(iter->rawData(), maskValue, 1);
iter->nextPixel();
}
iter->nextRow();
}
m_maskBounds = QRect(0, 0, width, height);
}
void KisTextureProperties::fillProperties(const KisPropertiesConfiguration *setting)
{
if (!setting->hasProperty("Texture/Pattern/PatternMD5")) {
m_enabled = false;
return;
}
m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting);
if (!m_pattern) {
- qWarning() << "WARNING: Couldn't load the pattern for a stroke";
+ warnKrita << "WARNING: Couldn't load the pattern for a stroke";
m_enabled = false;
return;
}
m_enabled = setting->getBool("Texture/Pattern/Enabled", false);
m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0);
m_offsetX = setting->getInt("Texture/Pattern/OffsetX");
m_offsetY = setting->getInt("Texture/Pattern/OffsetY");
m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY);
m_invert = setting->getBool("Texture/Pattern/Invert");
m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0);
m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255);
m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0);
m_strengthOption.readOptionSetting(setting);
m_strengthOption.resetAllSensors();
recalculateMask();
}
void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info)
{
if (!m_enabled) return;
KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
QRect rect = dab->bounds();
int x = offset.x() % m_maskBounds.width() - m_offsetX;
int y = offset.y() % m_maskBounds.height() - m_offsetY;
KisFillPainter fillPainter(fillDevice);
fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, m_mask, m_maskBounds);
fillPainter.end();
qreal pressure = m_strengthOption.apply(info);
quint8 *dabData = dab->data();
KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
for (int row = 0; row < rect.height(); ++row) {
for (int col = 0; col < rect.width(); ++col) {
if (m_texturingMode == MULTIPLY) {
dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1);
}
else {
int pressureOffset = (1.0 - pressure) * 255;
qint16 maskA = *iter->oldRawData() + pressureOffset;
quint8 dabA = dab->colorSpace()->opacityU8(dabData);
dabA = qMax(0, (qint16)dabA - maskA);
dab->colorSpace()->setOpacity(dabData, dabA, 1);
}
iter->nextPixel();
dabData += dab->pixelSize();
}
iter->nextRow();
}
}
diff --git a/krita/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp b/krita/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
index 4af0b393fa5..8136c60efb0 100644
--- a/krita/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
+++ b/krita/plugins/paintops/sketch/kis_sketch_paintop_settings.cpp
@@ -1,83 +1,83 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_sketch_paintop_settings.h"
#include <kis_sketchop_option.h>
#include <kis_paint_action_type_option.h>
#include <kis_airbrush_option.h>
#include "kis_current_outline_fetcher.h"
KisSketchPaintOpSettings::KisSketchPaintOpSettings()
{
}
bool KisSketchPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
bool KisSketchPaintOpSettings::isAirbrushing() const
{
return getBool(AIRBRUSH_ENABLED);
}
int KisSketchPaintOpSettings::rate() const
{
return getInt(AIRBRUSH_RATE);
}
QPainterPath KisSketchPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
bool isSimpleMode = getBool(SKETCH_USE_SIMPLE_MODE);
if (!isSimpleMode) {
return KisBrushBasedPaintOpSettings::brushOutline(info, mode);
}
KisBrushBasedPaintopOptionWidget *widget = dynamic_cast<KisBrushBasedPaintopOptionWidget*>(optionsWidget());
QPainterPath path;
if (widget && (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline)) {
KisBrushSP brush = widget->brush();
// just circle supported
qreal diameter = qMax(brush->width(), brush->height());
path = ellipseOutline(diameter, diameter, 1.0, 0.0/*brush->scale(), brush->angle()*/);
QPainterPath tiltLine;
QLineF tiltAngle(path.boundingRect().center(), path.boundingRect().topLeft());
- tiltAngle.setLength(qMax(diameter*0.5, qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
+ tiltAngle.setLength(qMax(diameter*qreal(0.5), qreal(50.0)) * (1 - info.tiltElevation(info, 60.0, 60.0, true)));
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))-3.0);
tiltLine.moveTo(tiltAngle.p1());
tiltLine.lineTo(tiltAngle.p2());
tiltAngle.setAngle((360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0))+3.0);
tiltLine.lineTo(tiltAngle.p2());
tiltLine.lineTo(tiltAngle.p1());
path = outlineFetcher()->fetchOutline(info, this, path);
if (mode == CursorTiltOutline) {
QPainterPath realOutline = brush->outline();
tiltLine.translate(info.pos());
path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, 1.0, 0.0, true, path.boundingRect().center().x(), path.boundingRect().center().y()));
}
}
return path;
}
diff --git a/krita/plugins/paintops/spray/kis_spray_paintop.cpp b/krita/plugins/paintops/spray/kis_spray_paintop.cpp
index a31b270ccbb..afdedf60eb1 100644
--- a/krita/plugins/paintops/spray/kis_spray_paintop.cpp
+++ b/krita/plugins/paintops/spray/kis_spray_paintop.cpp
@@ -1,129 +1,129 @@
/*
* Copyright (c) 2008-2012 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_spray_paintop.h"
#include "kis_spray_paintop_settings.h"
#include <cmath>
#include <QRect>
#include <kis_global.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include <kis_types.h>
#include <kis_paintop.h>
#include <kis_pressure_rotation_option.h>
#include <kis_pressure_opacity_option.h>
#include <kis_pressure_size_option.h>
#include <kis_fixed_paint_device.h>
#include <kis_brush_option.h>
#include <kis_sprayop_option.h>
#include <kis_spray_shape_option.h>
#include <kis_color_option.h>
KisSprayPaintOp::KisSprayPaintOp(const KisSprayPaintOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
: KisPaintOp(painter)
, m_settings(settings)
, m_isPresetValid(true)
, m_node(node)
{
Q_ASSERT(settings);
Q_ASSERT(painter);
Q_UNUSED(image);
m_rotationOption.readOptionSetting(settings);
m_opacityOption.readOptionSetting(settings);
m_sizeOption.readOptionSetting(settings);
m_rotationOption.resetAllSensors();
m_opacityOption.resetAllSensors();
m_sizeOption.resetAllSensors();
m_brushOption.readOptionSetting(settings);
m_colorProperties.fillProperties(settings);
m_properties.loadSettings(settings);
// first load tip properties as shape properties are dependent on diameter/scale/aspect
m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale);
// TODO: what to do with proportional sizes?
m_shapeDynamicsProperties.loadSettings(settings);
if (!m_shapeProperties.enabled && !m_brushOption.brush()) {
// in case the preset does not contain the definition for KisBrush
m_isPresetValid = false;
- kWarning() << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset.";
+ dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset.";
}
m_sprayBrush.setProperties(&m_properties, &m_colorProperties,
&m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush());
m_sprayBrush.setFixedDab(cachedDab());
// spacing
if ((m_properties.diameter * 0.5) > 1) {
m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing;
}
else {
m_ySpacing = m_xSpacing = 1.0;
}
m_spacing = m_xSpacing;
}
KisSprayPaintOp::~KisSprayPaintOp()
{
}
KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info)
{
if (!painter() || !m_isPresetValid) {
return KisSpacingInformation(m_spacing);
}
if (!m_dab) {
m_dab = source()->createCompositionSourceDevice();
}
else {
m_dab->clear();
}
qreal rotation = m_rotationOption.apply(info);
quint8 origOpacity = m_opacityOption.apply(painter(), info);
// Spray Brush is capable of working with zero scale,
// so no additional checks for 'zero'ness are needed
qreal scale = m_sizeOption.apply(info);
setCurrentRotation(rotation);
setCurrentScale(scale);
m_sprayBrush.paint(m_dab,
m_node->paintDevice(),
info,
rotation,
scale,
painter()->paintColor(),
painter()->backgroundColor());
QRect rc = m_dab->extent();
painter()->bitBlt(rc.topLeft(), m_dab, rc);
painter()->renderMirrorMask(rc, m_dab);
painter()->setOpacity(origOpacity);
return KisSpacingInformation(m_spacing);
}
diff --git a/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp b/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp
index aeb4128679c..c83ec1f2b46 100644
--- a/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp
+++ b/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.cpp
@@ -1,271 +1,271 @@
/* This file is part of the KDE project
*
* Copyright (C) 2015 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_tangent_tilt_option.h"
#include <cmath>
#include <QColor>
#include <QPoint>
#include "ui_wdgtangenttiltoption.h"
#include "kis_global.h"
#include <kstandarddirs.h>
#include "kis_factory2.h"
class KisTangentTiltOptionWidget: public QWidget, public Ui::WdgTangentTiltOptions
{
public:
KisTangentTiltOptionWidget(QWidget *parent = 0)
: QWidget(parent) {
setupUi(this);
}
};
KisTangentTiltOption::KisTangentTiltOption()
: KisPaintOpOption(KisPaintOpOption::GENERAL, false),
m_canvasAngle(0.0),
m_canvasAxisXMirrored(false),
m_canvasAxisYMirrored(false)
{
m_checkable = false;
m_options = new KisTangentTiltOptionWidget();
//Setup tangent tilt.
m_options->comboRed->setCurrentIndex(0);
m_options->comboGreen->setCurrentIndex(2);
m_options->comboBlue->setCurrentIndex(4);
m_options->sliderElevationSensitivity->setRange(0, 100, 0);
m_options->sliderElevationSensitivity->setValue(100);
m_options->sliderElevationSensitivity->setSuffix("%");
m_options->sliderMixValue->setRange(0, 100, 0);
m_options->sliderMixValue->setValue(50);
m_options->sliderMixValue->setSuffix("%");
connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
connect(m_options->comboRed, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setRedChannel(int) ));
connect(m_options->comboGreen, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setGreenChannel(int) ));
connect(m_options->comboBlue, SIGNAL(currentIndexChanged(int)), m_options->TangentTiltPreview, SLOT(setBlueChannel(int) ));
connect(m_options->optionTilt, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->optionDirection, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->optionRotation, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->optionMix, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_options->sliderElevationSensitivity, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
connect(m_options->sliderMixValue, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged()));
m_options->sliderMixValue->setVisible(false);
setConfigurationPage(m_options);
}
KisTangentTiltOption::~KisTangentTiltOption()
{
delete m_options;
}
//options
int KisTangentTiltOption::redChannel() const
{
return m_options->comboRed->currentIndex();
}
int KisTangentTiltOption::greenChannel() const
{
return m_options->comboGreen->currentIndex();
}
int KisTangentTiltOption::blueChannel() const
{
return m_options->comboBlue->currentIndex();
}
int KisTangentTiltOption::directionType() const
{
int type=0;
if (m_options->optionTilt->isChecked()==true) {
type=0;
}
else if (m_options->optionDirection->isChecked()==true) {
type=1;
}
else if (m_options->optionRotation->isChecked()==true) {
type=2;
}
else if (m_options->optionMix->isChecked()==true) {
type=3;
}
else {
- qWarning()<<"There's something odd with the radio buttons. We'll use Tilt";
+ warnKrita<<"There's something odd with the radio buttons. We'll use Tilt";
}
return type;
}
double KisTangentTiltOption::elevationSensitivity() const
{
return m_options->sliderElevationSensitivity->value();
}
double KisTangentTiltOption::mixValue() const
{
return m_options->sliderMixValue->value();
}
void KisTangentTiltOption::swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, quint8 *component, int index, qreal maxvalue)
{
switch(index) {
case 0: *component = horizontal; break;
case 1: *component = maxvalue-horizontal; break;
case 2: *component = vertical; break;
case 3: *component = maxvalue-vertical; break;
case 4: *component = depth; break;
case 5: *component = maxvalue-depth; break;
}
}
void KisTangentTiltOption::apply(const KisPaintInformation& info,quint8 *r,quint8 *g,quint8 *b)
{
//formula based on http://www.cerebralmeltdown.com/programming_projects/Altitude%20and%20Azimuth%20to%20Vector/index.html
/* It doesn't make sense of have higher than 8bit color depth.
* Instead we make sure in the paintAt function of kis_tangent_normal_paintop to pick an 8bit space of the current
* color space if the space is an RGB space. If not, it'll pick sRGB 8bit.
*/
qreal halfvalue = 128;
qreal maxvalue = 255;
//have the azimuth and altitude in degrees.
qreal direction = KisPaintInformation::tiltDirection(info, true)*360.0;
qreal elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);
if (directionType()==0) {
direction = KisPaintInformation::tiltDirection(info, true)*360.0;
elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);
} else if (directionType()==1) {
direction = (0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0;
elevation= 0;//turns out that tablets that don't support tilt just return 90 degrees for elevation.
} else if (directionType()==2) {
direction = info.rotation();
elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);//artpens have tilt-recognition, so this should work.
} else if (directionType()==3) {//mix of tilt+direction
qreal mixamount = mixValue()/100.0;
direction = (KisPaintInformation::tiltDirection(info, true)*360.0*(1.0-mixamount))+((0.75 + info.drawingAngle() / (2.0 * M_PI))*360.0*(mixamount));
elevation= (info.tiltElevation(info, 60.0, 60.0, true)*90.0);
}
//subtract/add the rotation of the canvas.
if (info.canvasRotation()!=m_canvasAngle && info.canvasMirroredH()==m_canvasAxisXMirrored) {
m_canvasAngle=info.canvasRotation();
}
if (directionType()!=1) {
direction = direction-m_canvasAngle;
}
//limit the direction/elevation
//qreal elevationMax = (elevationSensitivity()*90.0)/100.0;
qreal elevationT = elevation*(elevationSensitivity()/100.0)+(90-(elevationSensitivity()*90.0)/100.0);
elevation = static_cast<int>(elevationT);
//convert to radians.
// Convert this to kis_global's radian function.
direction = kisDegreesToRadians(direction);
elevation = kisDegreesToRadians(elevation);
//make variables for axes for easy switching later on.
qreal horizontal, vertical, depth;
//spherical coordinates always center themselves around the origin, leading to values. We need to work around those...
horizontal = cos(elevation)*sin(direction);
if (horizontal>0.0) {
horizontal= halfvalue+(fabs(horizontal)*halfvalue);
}
else {
horizontal= halfvalue-(fabs(horizontal)*halfvalue);
}
vertical = cos(elevation)*cos(direction);
if (vertical>0.0) {
vertical = halfvalue+(fabs(vertical)*halfvalue);
}
else {
vertical = halfvalue-(fabs(vertical)*halfvalue);
}
if (m_canvasAxisXMirrored && info.canvasMirroredH()) {horizontal = maxvalue-horizontal;}
if (m_canvasAxisYMirrored && info.canvasMirroredH()) {vertical = maxvalue-vertical;}
depth = sin(elevation)*maxvalue;
//assign right components to correct axes.
swizzleAssign(horizontal, vertical, depth, r, redChannel(), maxvalue);
swizzleAssign(horizontal, vertical, depth, g, greenChannel(), maxvalue);
swizzleAssign(horizontal, vertical, depth, b, blueChannel(), maxvalue);
}
/*settings*/
void KisTangentTiltOption::writeOptionSetting(KisPropertiesConfiguration* setting) const
{
setting->setProperty(TANGENT_RED, redChannel());
setting->setProperty(TANGENT_GREEN, greenChannel());
setting->setProperty(TANGENT_BLUE, blueChannel());
setting->setProperty(TANGENT_TYPE, directionType());
setting->setProperty(TANGENT_EV_SEN, elevationSensitivity());
setting->setProperty(TANGENT_MIX_VAL, mixValue());
}
void KisTangentTiltOption::readOptionSetting(const KisPropertiesConfiguration* setting)
{
m_options->comboRed->setCurrentIndex(setting->getInt(TANGENT_RED, 0));
m_options->comboGreen->setCurrentIndex(setting->getInt(TANGENT_GREEN, 2));
m_options->comboBlue->setCurrentIndex(setting->getInt(TANGENT_BLUE, 4));
//The comboboxes are connected to the TangentTiltPreview, so that gets automatically updated by them.
if (setting->getInt(TANGENT_TYPE)== 0){
m_options->optionTilt->setChecked(true);
m_options->sliderMixValue->setVisible(false);
}
else if (setting->getInt(TANGENT_TYPE)== 1) {
m_options->optionDirection->setChecked(true);
m_options->sliderMixValue->setVisible(false);
}
else if (setting->getInt(TANGENT_TYPE)== 2) {
m_options->optionRotation->setChecked(true);
m_options->sliderMixValue->setVisible(false);
}
else if (setting->getInt(TANGENT_TYPE)== 3) {
m_options->optionMix->setChecked(true);
m_options->sliderMixValue->setVisible(true);
}
m_canvasAngle = setting->getDouble("runtimeCanvasRotation", 0.0);//in degrees please.
m_canvasAxisXMirrored = setting->getBool("runtimeCanvasMirroredX", false);
m_canvasAxisYMirrored = setting->getBool("runtimeCanvasMirroredY", false);
m_options->sliderElevationSensitivity->setValue(setting->getDouble(TANGENT_EV_SEN, 100));
m_options->sliderMixValue->setValue(setting->getDouble(TANGENT_MIX_VAL, 50));
}
diff --git a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
index 7b118710f2a..63aec3ddb17 100644
--- a/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
+++ b/krita/plugins/tools/selectiontools/kis_tool_select_contiguous.cc
@@ -1,234 +1,253 @@
/*
* kis_tool_select_contiguous - part of Krayon^WKrita
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2012 José Luis Vergara <pentalis@gmail.com>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_select_contiguous.h"
#include <QPainter>
#include <QLayout>
#include <QLabel>
#include <QApplication>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <kis_debug.h>
#include <klocale.h>
#include <kglobal.h>
#include "KoPointerEvent.h"
#include "KoViewConverter.h"
#include "kis_cursor.h"
#include "kis_selection_manager.h"
#include "kis_image.h"
#include "canvas/kis_canvas2.h"
#include "kis_layer.h"
#include "kis_selection_options.h"
#include "kis_paint_device.h"
#include "kis_fill_painter.h"
#include "kis_pixel_selection.h"
#include "kis_selection_tool_helper.h"
#include "kis_slider_spin_box.h"
+#include "kis_paint_device.h"
+#include "kis_pixel_selection.h"
+#include "tiles3/kis_hline_iterator.h"
KisToolSelectContiguous::KisToolSelectContiguous(KoCanvasBase *canvas)
: KisToolSelectBase(canvas,
KisCursor::load("tool_contiguous_selection_cursor.png", 6, 6),
i18n("Contiguous Area Selection")),
m_fuzziness(20),
m_sizemod(0),
m_feather(0),
m_limitToCurrentLayer(false)
{
setObjectName("tool_select_contiguous");
connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(setSelectionAction(int)));
}
KisToolSelectContiguous::~KisToolSelectContiguous()
{
}
void KisToolSelectContiguous::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
m_configGroup = KGlobal::config()->group(toolId());
}
void KisToolSelectContiguous::beginPrimaryAction(KoPointerEvent *event)
{
KisToolSelectBase::beginPrimaryAction(event);
KisPaintDeviceSP dev;
if (!currentNode() ||
!(dev = currentNode()->projection()) ||
!currentNode()->visible() ||
!selectionEditable()) {
event->ignore();
return;
}
QApplication::setOverrideCursor(KisCursor::waitCursor());
QPoint pos = convertToIntPixelCoord(event);
QRect rc = currentImage()->bounds();
KisFillPainter fillpainter(dev);
fillpainter.setHeight(rc.height());
fillpainter.setWidth(rc.width());
fillpainter.setFillThreshold(m_fuzziness);
KisImageWSP image = currentImage();
KisPaintDeviceSP sourceDevice = m_limitToCurrentLayer ? dev : image->projection();
image->lock();
fillpainter.setFeather(m_feather);
fillpainter.setSizemod(m_sizemod);
KisSelectionSP selection = fillpainter.createFloodSelection(pos.x(), pos.y(), sourceDevice);
image->unlock();
+ // If we're not antialiasing, threshold the entire selection
+ if (!antiAliasSelection()) {
+ QRect r = selection->selectedExactRect();
+ if (r.isValid()) {
+ KisHLineIteratorSP selectionIt = selection->pixelSelection()->createHLineIteratorNG(r.x(), r.y(), r.width());
+ for (qint32 y = 0; y < r.height(); y++) {
+ do {
+ if (selectionIt->rawData()[0] > 0) {
+ selection->pixelSelection()->colorSpace()->setOpacity(selectionIt->rawData(), OPACITY_OPAQUE_U8, 1);
+ }
+ } while (selectionIt->nextPixel());
+ selectionIt->nextRow();
+ }
+ }
+ }
+
+
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
if (!kisCanvas || !selection->pixelSelection()) {
QApplication::restoreOverrideCursor();
return;
}
selection->pixelSelection()->invalidateOutlineCache();
KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Contiguous Area"));
helper.selectPixelSelection(selection->pixelSelection(), selectionAction());
QApplication::restoreOverrideCursor();
}
void KisToolSelectContiguous::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_UNUSED(painter);
Q_UNUSED(converter);
}
void KisToolSelectContiguous::slotSetFuzziness(int fuzziness)
{
m_fuzziness = fuzziness;
m_configGroup.writeEntry("fuzziness", fuzziness);
}
void KisToolSelectContiguous::slotSetSizemod(int sizemod)
{
m_sizemod = sizemod;
m_configGroup.writeEntry("sizemod", sizemod);
}
void KisToolSelectContiguous::slotSetFeather(int feather)
{
m_feather = feather;
m_configGroup.writeEntry("feather", feather);
}
QWidget* KisToolSelectContiguous::createOptionWidget()
{
KisToolSelectBase::createOptionWidget();
KisSelectionOptions *selectionWidget = selectionOptionWidget();
- selectionWidget->disableAntiAliasSelectionOption();
selectionWidget->disableSelectionModeOption();
QVBoxLayout * l = dynamic_cast<QVBoxLayout*>(selectionWidget->layout());
Q_ASSERT(l);
if (l) {
QHBoxLayout * hbox = new QHBoxLayout();
Q_CHECK_PTR(hbox);
l->insertLayout(1, hbox);
QLabel * lbl = new QLabel(i18n("Fuzziness: "), selectionWidget);
hbox->addWidget(lbl);
KisSliderSpinBox *input = new KisSliderSpinBox(selectionWidget);
Q_CHECK_PTR(input);
input->setObjectName("fuzziness");
input->setRange(0, 200);
input->setSingleStep(10);
hbox->addWidget(input);
hbox = new QHBoxLayout();
Q_CHECK_PTR(hbox);
l->insertLayout(2, hbox);
lbl = new QLabel(i18n("Grow/shrink selection: "), selectionWidget);
hbox->addWidget(lbl);
KisSliderSpinBox *sizemod = new KisSliderSpinBox(selectionWidget);
Q_CHECK_PTR(sizemod);
sizemod->setObjectName("sizemod"); //grow/shrink selection
sizemod->setRange(-40, 40);
sizemod->setSingleStep(1);
hbox->addWidget(sizemod);
hbox = new QHBoxLayout();
Q_CHECK_PTR(hbox);
l->insertLayout(3, hbox);
hbox->addWidget(new QLabel(i18n("Feathering radius: "), selectionWidget));
KisSliderSpinBox *feather = new KisSliderSpinBox(selectionWidget);
Q_CHECK_PTR(feather);
feather->setObjectName("feathering");
feather->setRange(0, 40);
feather->setSingleStep(1);
hbox->addWidget(feather);
connect (input , SIGNAL(valueChanged(int)), this, SLOT(slotSetFuzziness(int) ));
connect (sizemod, SIGNAL(valueChanged(int)), this, SLOT(slotSetSizemod(int) ));
connect (feather, SIGNAL(valueChanged(int)), this, SLOT(slotSetFeather(int) ));
QCheckBox* limitToCurrentLayer = new QCheckBox(i18n("Limit to current layer"), selectionWidget);
l->insertWidget(4, limitToCurrentLayer);
connect (limitToCurrentLayer, SIGNAL(stateChanged(int)), this, SLOT(slotLimitToCurrentLayer(int)));
// load configuration settings into tool options
input->setValue(m_configGroup.readEntry("fuzziness", 20)); // fuzziness
sizemod->setValue( m_configGroup.readEntry("sizemod", 0)); //grow/shrink
feather->setValue(m_configGroup.readEntry("feather", 0));
limitToCurrentLayer->setChecked(m_configGroup.readEntry("limitToCurrentLayer", false));
}
return selectionWidget;
}
void KisToolSelectContiguous::slotLimitToCurrentLayer(int state)
{
if (state == Qt::PartiallyChecked)
return;
m_limitToCurrentLayer = (state == Qt::Checked);
m_configGroup.writeEntry("limitToCurrentLayer", state);
}
void KisToolSelectContiguous::setSelectionAction(int newSelectionAction)
{
if(newSelectionAction >= SELECTION_REPLACE && newSelectionAction <= SELECTION_INTERSECT && m_selectionAction != newSelectionAction)
{
if(m_widgetHelper.optionWidget())
{
m_widgetHelper.slotSetAction(newSelectionAction);
}
m_selectionAction = (SelectionAction)newSelectionAction;
emit selectionActionChanged();
}
}
diff --git a/krita/plugins/tools/tool_transform2/kis_free_transform_strategy_gsl_helpers.cpp b/krita/plugins/tools/tool_transform2/kis_free_transform_strategy_gsl_helpers.cpp
index c6d7d93ed19..31524d136d9 100644
--- a/krita/plugins/tools/tool_transform2/kis_free_transform_strategy_gsl_helpers.cpp
+++ b/krita/plugins/tools/tool_transform2/kis_free_transform_strategy_gsl_helpers.cpp
@@ -1,390 +1,390 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_free_transform_strategy_gsl_helpers.h"
#include "tool_transform_args.h"
#include "kis_transform_utils.h"
#include <QMessageBox>
#include <config-gsl.h>
#ifdef HAVE_GSL
#include <gsl/gsl_multimin.h>
namespace GSL
{
struct YScaleStrategy {
static qreal getScale(const ToolTransformArgs &args) {
return args.scaleY();
}
static void setScale(ToolTransformArgs *args, qreal scale) {
return args->setScaleY(scale);
}
};
struct XScaleStrategy {
static qreal getScale(const ToolTransformArgs &args) {
return args.scaleX();
}
static void setScale(ToolTransformArgs *args, qreal scale) {
return args->setScaleX(scale);
}
};
struct Params1D {
QPointF staticPointSrc;
QPointF staticPointDst;
QPointF movingPointSrc;
qreal viewDistance;
const ToolTransformArgs *srcArgs;
};
template <class Strategy>
double scaleError1D (const gsl_vector * x, void *paramsPtr)
{
double scale = gsl_vector_get(x, 0);
double tX = gsl_vector_get(x, 1);
double tY = gsl_vector_get(x, 2);
const Params1D *params = static_cast<const Params1D*>(paramsPtr);
ToolTransformArgs args(*params->srcArgs);
Strategy::setScale(&args, scale);
args.setTransformedCenter(QPointF(tX, tY));
KisTransformUtils::MatricesPack m(args);
QTransform t = m.finalTransform();
QPointF transformedStaticPoint = t.map(params->staticPointSrc);
QPointF transformedMovingPoint = t.map(params->movingPointSrc);
qreal result =
qAbs(kisDistance(transformedStaticPoint, transformedMovingPoint)
- params->viewDistance) +
qAbs(transformedStaticPoint.x() - params->staticPointDst.x()) +
qAbs(transformedStaticPoint.y() - params->staticPointDst.y());
return result;
}
template <class Strategy>
ScaleResult1D calculateScale1D(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
{
const gsl_multimin_fminimizer_type *T =
gsl_multimin_fminimizer_nmsimplex2;
gsl_multimin_fminimizer *s = NULL;
gsl_vector *ss, *x;
gsl_multimin_function minex_func;
size_t iter = 0;
int status;
double size;
/* Starting point */
x = gsl_vector_alloc (3);
gsl_vector_set (x, 0, Strategy::getScale(args));
gsl_vector_set (x, 1, args.transformedCenter().x());
gsl_vector_set (x, 2, args.transformedCenter().y());
/* Set initial step sizes to 0.1 */
ss = gsl_vector_alloc (3);
gsl_vector_set (ss, 0, 0.1);
gsl_vector_set (ss, 1, 10);
gsl_vector_set (ss, 2, 10);
Params1D p;
p.staticPointSrc = staticPointSrc;
p.staticPointDst = staticPointDst;
p.movingPointSrc = movingPointSrc;
p.viewDistance = viewDistance;
p.srcArgs = &args;
/* Initialize method and iterate */
minex_func.n = 3;
minex_func.f = scaleError1D<Strategy>;
minex_func.params = (void*)&p;
s = gsl_multimin_fminimizer_alloc (T, 3);
gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
ScaleResult1D result;
result.scale = Strategy::getScale(args);
result.transformedCenter = args.transformedCenter();
do
{
iter++;
status = gsl_multimin_fminimizer_iterate(s);
if (status)
break;
size = gsl_multimin_fminimizer_size (s);
status = gsl_multimin_test_size (size, 1e-6);
if (status == GSL_SUCCESS)
{
- // qDebug() << "*******Converged to minimum";
- // qDebug() << gsl_vector_get (s->x, 0)
+ // dbgKrita << "*******Converged to minimum";
+ // dbgKrita << gsl_vector_get (s->x, 0)
// << gsl_vector_get (s->x, 1)
// << gsl_vector_get (s->x, 2)
// << "|" << s->fval << size;
result.scale = gsl_vector_get (s->x, 0);
result.transformedCenter =
QPointF(gsl_vector_get (s->x, 1),
gsl_vector_get (s->x, 2));
}
}
while (status == GSL_CONTINUE && iter < 10000);
gsl_vector_free(x);
gsl_vector_free(ss);
gsl_multimin_fminimizer_free (s);
return result;
}
struct Params2D {
QPointF staticPointSrc;
QPointF staticPointDst;
QPointF movingPointSrc;
QPointF movingPointDst;
const ToolTransformArgs *srcArgs;
};
double scaleError2D (const gsl_vector * x, void *paramsPtr)
{
double scaleX = gsl_vector_get(x, 0);
double scaleY = gsl_vector_get(x, 1);
double tX = gsl_vector_get(x, 2);
double tY = gsl_vector_get(x, 3);
const Params2D *params = static_cast<const Params2D*>(paramsPtr);
ToolTransformArgs args(*params->srcArgs);
args.setScaleX(scaleX);
args.setScaleY(scaleY);
args.setTransformedCenter(QPointF(tX, tY));
KisTransformUtils::MatricesPack m(args);
QTransform t = m.finalTransform();
QPointF transformedStaticPoint = t.map(params->staticPointSrc);
QPointF transformedMovingPoint = t.map(params->movingPointSrc);
qreal result =
qAbs(transformedMovingPoint.x() - params->movingPointDst.x()) +
qAbs(transformedMovingPoint.y() - params->movingPointDst.y()) +
qAbs(transformedStaticPoint.x() - params->staticPointDst.x()) +
qAbs(transformedStaticPoint.y() - params->staticPointDst.y());
return result;
}
ScaleResult2D calculateScale2D(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
const QPointF &movingPointDst)
{
const gsl_multimin_fminimizer_type *T =
gsl_multimin_fminimizer_nmsimplex2;
gsl_multimin_fminimizer *s = NULL;
gsl_vector *ss, *x;
gsl_multimin_function minex_func;
size_t iter = 0;
int status;
double size;
/* Starting point */
x = gsl_vector_alloc (4);
gsl_vector_set (x, 0, args.scaleX());
gsl_vector_set (x, 1, args.scaleY());
gsl_vector_set (x, 2, args.transformedCenter().x());
gsl_vector_set (x, 3, args.transformedCenter().y());
/* Set initial step sizes to 0.1 */
ss = gsl_vector_alloc (4);
gsl_vector_set (ss, 0, 0.1);
gsl_vector_set (ss, 1, 0.1);
gsl_vector_set (ss, 2, 10);
gsl_vector_set (ss, 3, 10);
Params2D p;
p.staticPointSrc = staticPointSrc;
p.staticPointDst = staticPointDst;
p.movingPointSrc = movingPointSrc;
p.movingPointDst = movingPointDst;
p.srcArgs = &args;
/* Initialize method and iterate */
minex_func.n = 4;
minex_func.f = scaleError2D;
minex_func.params = (void*)&p;
s = gsl_multimin_fminimizer_alloc (T, 4);
gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
ScaleResult2D result;
result.scaleX = args.scaleX();
result.scaleY = args.scaleY();
result.transformedCenter = args.transformedCenter();
do
{
iter++;
status = gsl_multimin_fminimizer_iterate(s);
if (status)
break;
size = gsl_multimin_fminimizer_size (s);
status = gsl_multimin_test_size (size, 1e-6);
if (status == GSL_SUCCESS)
{
- // qDebug() << "*******Converged to minimum";
- // qDebug() << gsl_vector_get (s->x, 0)
+ // dbgKrita << "*******Converged to minimum";
+ // dbgKrita << gsl_vector_get (s->x, 0)
// << gsl_vector_get (s->x, 1)
// << gsl_vector_get (s->x, 2)
// << gsl_vector_get (s->x, 3)
// << "|" << s->fval << size;
result.scaleX = gsl_vector_get (s->x, 0);
result.scaleY = gsl_vector_get (s->x, 1);
result.transformedCenter =
QPointF(gsl_vector_get (s->x, 2),
gsl_vector_get (s->x, 3));
}
}
while (status == GSL_CONTINUE && iter < 10000);
gsl_vector_free(x);
gsl_vector_free(ss);
gsl_multimin_fminimizer_free (s);
return result;
}
ScaleResult1D calculateScaleX(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
{
return calculateScale1D<XScaleStrategy>(args,
staticPointSrc,
staticPointDst,
movingPointSrc,
viewDistance);
}
ScaleResult1D calculateScaleY(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
{
return calculateScale1D<YScaleStrategy>(args,
staticPointSrc,
staticPointDst,
movingPointSrc,
viewDistance);
}
}
#else /* HAVE_GSL */
namespace GSL
{
void warnNoGSL()
{
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Sorry, Krita was built without the support "
"of GNU Scientific Library, so you cannot scale "
"the selection with handles. Please compile "
"Krita with GNU Scientific Library support, or use "
"options widget for editing scale values manually."));
}
ScaleResult2D calculateScale2D(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
const QPointF &movingPointDst)
{
warnNoGSL();
ScaleResult2D result;
result.scaleX = args.scaleX();
result.scaleY = args.scaleY();
result.transformedCenter = args.transformedCenter();
return result;
}
ScaleResult1D calculateScaleX(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
{
warnNoGSL();
ScaleResult1D result;
result.scale = args.scaleX();
result.transformedCenter = args.transformedCenter();
return result;
}
ScaleResult1D calculateScaleY(const ToolTransformArgs &args,
const QPointF &staticPointSrc,
const QPointF &staticPointDst,
const QPointF &movingPointSrc,
qreal viewDistance)
{
warnNoGSL();
ScaleResult1D result;
result.scale = args.scaleY();
result.transformedCenter = args.transformedCenter();
return result;
}
}
#endif /* HAVE_GSL */
diff --git a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
index fcb984d6e22..e128eaca19e 100644
--- a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
+++ b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
@@ -1,364 +1,364 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "transform_stroke_strategy.h"
#include <QMutexLocker>
#include "kundo2commandextradata.h"
#include "kis_node_progress_proxy.h"
#include <klocale.h>
#include <kis_node.h>
#include <kis_external_layer_iface.h>
#include <kis_transaction.h>
#include <kis_painter.h>
#include <kis_transform_worker.h>
#include <kis_transform_mask.h>
#include "kis_transform_mask_adapter.h"
#include "kis_transform_utils.h"
#include "kis_projection_leaf.h"
class ModifyTransformMaskCommand : public KUndo2Command {
public:
ModifyTransformMaskCommand(KisTransformMaskSP mask, KisTransformMaskParamsInterfaceSP params)
: m_mask(mask),
m_params(params),
m_oldParams(m_mask->transformParams())
{
}
void redo() {
m_mask->setTransformParams(m_params);
/**
* NOTE: this code "duplicates" the functionality provided
* by KisRecalculateTransformMaskJob, but there is not much
* reason for starting a separate stroke when a transformation
* has happened
*/
m_mask->recaclulateStaticImage();
updateMask();
}
void undo() {
m_mask->setTransformParams(m_oldParams);
m_mask->recaclulateStaticImage();
updateMask();
}
private:
void updateMask() {
QRect updateRect = m_mask->extent();
KisNodeSP parent = m_mask->parent();
if (parent && parent->original()) {
updateRect |= parent->original()->defaultBounds()->bounds();
}
m_mask->setDirty(updateRect);
}
private:
KisTransformMaskSP m_mask;
KisTransformMaskParamsInterfaceSP m_params;
KisTransformMaskParamsInterfaceSP m_oldParams;
};
TransformStrokeStrategy::TransformStrokeStrategy(KisNodeSP rootNode,
KisSelectionSP selection,
KisPostExecutionUndoAdapter *undoAdapter)
: KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoAdapter),
m_selection(selection)
{
if (rootNode->childCount() || !rootNode->paintDevice()) {
KisPaintDeviceSP device;
if (KisTransformMask* tmask =
dynamic_cast<KisTransformMask*>(rootNode.data())) {
device = tmask->buildPreviewDevice();
/**
* When working with transform mask, selections are not
* taken into account.
*/
m_selection = 0;
} else {
rootNode->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
device = rootNode->projection();
}
m_previewDevice = createDeviceCache(device);
} else {
m_previewDevice = createDeviceCache(rootNode->paintDevice());
putDeviceCache(rootNode->paintDevice(), m_previewDevice);
}
Q_ASSERT(m_previewDevice);
m_savedRootNode = rootNode;
}
TransformStrokeStrategy::~TransformStrokeStrategy()
{
}
KisPaintDeviceSP TransformStrokeStrategy::previewDevice() const
{
return m_previewDevice;
}
KisSelectionSP TransformStrokeStrategy::realSelection() const
{
return m_selection;
}
KisPaintDeviceSP TransformStrokeStrategy::createDeviceCache(KisPaintDeviceSP dev)
{
KisPaintDeviceSP cache;
if (m_selection) {
QRect srcRect = m_selection->selectedExactRect();
cache = dev->createCompositionSourceDevice();
KisPainter gc(cache);
gc.setSelection(m_selection);
gc.bitBlt(srcRect.topLeft(), dev, srcRect);
} else {
cache = dev->createCompositionSourceDevice(dev);
}
return cache;
}
bool TransformStrokeStrategy::haveDeviceInCache(KisPaintDeviceSP src)
{
QMutexLocker l(&m_devicesCacheMutex);
return m_devicesCacheHash.contains(src.data());
}
void TransformStrokeStrategy::putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache)
{
QMutexLocker l(&m_devicesCacheMutex);
m_devicesCacheHash.insert(src.data(), cache);
}
KisPaintDeviceSP TransformStrokeStrategy::getDeviceCache(KisPaintDeviceSP src)
{
QMutexLocker l(&m_devicesCacheMutex);
KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data());
if (!cache) {
- qWarning() << "WARNING: Transform Stroke: the device is absent in cache!";
+ warnKrita << "WARNING: Transform Stroke: the device is absent in cache!";
}
return cache;
}
bool TransformStrokeStrategy::checkBelongsToSelection(KisPaintDeviceSP device) const
{
return m_selection &&
(device == m_selection->pixelSelection().data() ||
device == m_selection->projection().data());
}
void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
TransformData *td = dynamic_cast<TransformData*>(data);
ClearSelectionData *csd = dynamic_cast<ClearSelectionData*>(data);
if(td) {
m_savedTransformArgs = td->config;
if (td->destination == TransformData::PAINT_DEVICE) {
QRect oldExtent = td->node->extent();
KisPaintDeviceSP device = td->node->paintDevice();
if (device && !checkBelongsToSelection(device)) {
KisPaintDeviceSP cachedPortion = getDeviceCache(device);
Q_ASSERT(cachedPortion);
KisTransaction transaction(device);
KisProcessingVisitor::ProgressHelper helper(td->node);
transformAndMergeDevice(td->config, cachedPortion,
device, &helper);
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
td->node->setDirty(oldExtent | td->node->extent());
} if (KisExternalLayer *extLayer =
dynamic_cast<KisExternalLayer*>(td->node.data())) {
if (td->config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
td->config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
if (td->config.aX() || td->config.aY()) {
- qWarning() << "Perspective transform of an external layer is not supported:" << extLayer->name();
+ warnKrita << "Perspective transform of an external layer is not supported:" << extLayer->name();
}
QVector3D transformedCenter;
KisTransformWorker w = KisTransformUtils::createTransformWorker(td->config, 0, 0, &transformedCenter);
QTransform t = w.transform();
runAndSaveCommand(KUndo2CommandSP(extLayer->transform(t)),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (KisTransformMask *transformMask =
dynamic_cast<KisTransformMask*>(td->node.data())) {
runAndSaveCommand(KUndo2CommandSP(
new ModifyTransformMaskCommand(transformMask,
KisTransformMaskParamsInterfaceSP(
new KisTransformMaskAdapter(td->config)))),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (m_selection) {
/**
* We use usual transaction here, because we cannot calsulate
* transformation for perspective and warp workers.
*/
KisTransaction transaction(m_selection->pixelSelection());
KisProcessingVisitor::ProgressHelper helper(td->node);
KisTransformUtils::transformDevice(td->config,
m_selection->pixelSelection(),
&helper);
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (csd) {
KisPaintDeviceSP device = csd->node->paintDevice();
if (device && !checkBelongsToSelection(device)) {
if (!haveDeviceInCache(device)) {
putDeviceCache(device, createDeviceCache(device));
}
clearSelection(device);
} else if (KisTransformMask *transformMask =
dynamic_cast<KisTransformMask*>(csd->node.data())) {
runAndSaveCommand(KUndo2CommandSP(
new ModifyTransformMaskCommand(transformMask,
KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(true)))),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
} else {
KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
}
}
void TransformStrokeStrategy::clearSelection(KisPaintDeviceSP device)
{
KisTransaction transaction(device);
if (m_selection) {
device->clearSelection(m_selection);
} else {
QRect oldExtent = device->extent();
device->clear();
device->setDirty(oldExtent);
}
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &config,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisProcessingVisitor::ProgressHelper *helper)
{
KoUpdaterPtr mergeUpdater = src != dst ? helper->updater() : 0;
KisTransformUtils::transformDevice(config, src, helper);
if (src != dst) {
QRect mergeRect = src->extent();
KisPainter painter(dst);
painter.setProgress(mergeUpdater);
painter.bitBlt(mergeRect.topLeft(), src, mergeRect);
painter.end();
}
}
struct TransformExtraData : public KUndo2CommandExtraData
{
ToolTransformArgs savedTransformArgs;
KisNodeSP rootNode;
};
void TransformStrokeStrategy::postProcessToplevelCommand(KUndo2Command *command)
{
TransformExtraData *data = new TransformExtraData();
data->savedTransformArgs = m_savedTransformArgs;
data->rootNode = m_savedRootNode;
command->setExtraData(data);
}
bool TransformStrokeStrategy::fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode)
{
const TransformExtraData *data = dynamic_cast<const TransformExtraData*>(command->extraData());
if (data) {
*args = data->savedTransformArgs;
*rootNode = data->rootNode;
}
return bool(data);
}
void TransformStrokeStrategy::initStrokeCallback()
{
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
if (m_selection) {
m_selection->setVisible(false);
}
}
void TransformStrokeStrategy::finishStrokeCallback()
{
if (m_selection) {
m_selection->setVisible(true);
}
KisStrokeStrategyUndoCommandBased::finishStrokeCallback();
}
void TransformStrokeStrategy::cancelStrokeCallback()
{
KisStrokeStrategyUndoCommandBased::cancelStrokeCallback();
if (m_selection) {
m_selection->setVisible(true);
}
}
diff --git a/krita/sdk/tests/filestest.h b/krita/sdk/tests/filestest.h
index e950f473bf8..b284851a5e4 100644
--- a/krita/sdk/tests/filestest.h
+++ b/krita/sdk/tests/filestest.h
@@ -1,121 +1,121 @@
/*
* Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef FILESTEST
#define FILESTEST
#include "testutil.h"
#include <QDir>
#include <kaboutdata.h>
#include <klocale.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <kis_image.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KisPart.h>
#include <QTemporaryFile>
#include <QFileInfo>
namespace TestUtil
{
void testFiles(const QString& _dirname, const QStringList& exclusions, const QString &resultSuffix = QString(), int fuzzy = 0)
{
QDir dirSources(_dirname);
QStringList failuresFileInfo;
QStringList failuresDocImage;
QStringList failuresCompare;
foreach(QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
if (exclusions.indexOf(sourceFileInfo.fileName()) > -1) {
continue;
}
if (!sourceFileInfo.isHidden() && !sourceFileInfo.isDir()) {
QFileInfo resultFileInfo(QString(FILES_DATA_DIR) + "/results/" + sourceFileInfo.fileName() + resultSuffix + ".png");
if (!resultFileInfo.exists()) {
failuresFileInfo << resultFileInfo.fileName();
continue;
}
KisDocument *doc = qobject_cast<KisDocument*>(KisPart::instance()->createDocument());
KisImportExportManager manager(doc);
manager.setBatchMode(true);
KisImportExportFilter::ConversionStatus status;
QString s = manager.importDocument(sourceFileInfo.absoluteFilePath(), QString(),
status);
- qDebug() << s;
+ dbgKrita << s;
if (!doc->image()) {
failuresDocImage << sourceFileInfo.fileName();
continue;
}
QString id = doc->image()->colorSpace()->id();
if (id != "GRAYA" && id != "GRAYAU16" && id != "RGBA" && id != "RGBA16") {
dbgKrita << "Images need conversion";
doc->image()->convertImageColorSpace(KoColorSpaceRegistry::instance()->rgb8(),
KoColorConversionTransformation::IntentAbsoluteColorimetric,
KoColorConversionTransformation::NoOptimization);
}
QTemporaryFile tmpFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(".png"));
tmpFile.open();
doc->setBackupFile(false);
doc->setOutputMimeType("image/png");
doc->saveAs(KUrl("file://" + tmpFile.fileName()));
QImage resultImage(resultFileInfo.absoluteFilePath());
resultImage = resultImage.convertToFormat(QImage::Format_ARGB32);
QImage sourceImage(tmpFile.fileName());
sourceImage = sourceImage.convertToFormat(QImage::Format_ARGB32);
tmpFile.close();
QPoint pt;
if (!TestUtil::compareQImages(pt, resultImage, sourceImage, fuzzy)) {
failuresCompare << sourceFileInfo.fileName() + ": " + QString("Pixel (%1,%2) has different values").arg(pt.x()).arg(pt.y()).toLatin1();
resultImage.save(sourceFileInfo.fileName() + ".png");
continue;
}
delete doc;
}
}
if (failuresCompare.isEmpty() && failuresDocImage.isEmpty() && failuresFileInfo.isEmpty()) {
return;
}
- qDebug() << "Comparison failures: " << failuresCompare;
- qDebug() << "No image failures: " << failuresDocImage;
- qDebug() << "No comparison image: " << failuresFileInfo;
+ dbgKrita << "Comparison failures: " << failuresCompare;
+ dbgKrita << "No image failures: " << failuresDocImage;
+ dbgKrita << "No comparison image: " << failuresFileInfo;
QFAIL("Failed testing files");
}
}
#endif
diff --git a/krita/sdk/tests/qimage_based_test.h b/krita/sdk/tests/qimage_based_test.h
index 6eb4b0a3d8b..8c076b313a3 100644
--- a/krita/sdk/tests/qimage_based_test.h
+++ b/krita/sdk/tests/qimage_based_test.h
@@ -1,314 +1,314 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __QIMAGE_BASED_TEST_H
#define __QIMAGE_BASED_TEST_H
#include "testutil.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoShapeContainer.h>
#include <KoShapeRegistry.h>
#include "KisDocument.h"
#include "kis_shape_layer.h"
#include "kis_undo_stores.h"
#include "kis_image.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
#include "kis_adjustment_layer.h"
#include "kis_transparency_mask.h"
#include "kis_clone_layer.h"
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
#include "commands/kis_selection_commands.h"
namespace TestUtil
{
class QImageBasedTest
{
public:
QImageBasedTest(const QString &directoryName)
: m_directoryName(directoryName)
{
}
// you need to declare your own test function
// See KisProcessingTest for example
protected:
/**
* Creates a complex image connected to a surrogate undo store
*/
KisImageSP createImage(KisSurrogateUndoStore *undoStore) {
QImage sourceImage(fetchDataFileLazy("hakonepa.png"));
QRect imageRect = QRect(QPoint(0,0), sourceImage.size());
QRect transpRect(50,50,300,300);
QRect blurRect(66,66,300,300);
QPoint blurShift(34,34);
QPoint cloneShift(75,75);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "merge test");
KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
Q_ASSERT(filter);
KisFilterConfiguration *configuration = filter->defaultConfiguration(0);
Q_ASSERT(configuration);
KisAdjustmentLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration, 0);
blur1->internalSelection()->clear();
blur1->internalSelection()->pixelSelection()->select(blurRect);
blur1->setX(blurShift.x());
blur1->setY(blurShift.y());
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
paintLayer1->paintDevice()->convertFromQImage(sourceImage, 0, 0, 0);
KisTransparencyMaskSP transparencyMask1 = new KisTransparencyMask();
transparencyMask1->setName("tmask1");
transparencyMask1->testingInitSelection(transpRect);
KisCloneLayerSP cloneLayer1 =
new KisCloneLayer(paintLayer1, image, "clone1", OPACITY_OPAQUE_U8);
cloneLayer1->setX(cloneShift.x());
cloneLayer1->setY(cloneShift.y());
image->addNode(cloneLayer1);
image->addNode(blur1);
image->addNode(paintLayer1);
image->addNode(transparencyMask1, paintLayer1);
return image;
}
/**
* Creates a simple image with one empty layer and connects it to
* a surrogate undo store
*/
KisImageSP createTrivialImage(KisSurrogateUndoStore *undoStore) {
QRect imageRect = QRect(0, 0, 640, 441);
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "merge test");
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(paintLayer1);
return image;
}
void addGlobalSelection(KisImageSP image) {
QRect selectionRect(40,40,300,300);
KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(0, image));
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
pixelSelection->select(selectionRect);
KUndo2Command *cmd = new KisSetGlobalSelectionCommand(image, selection);
image->undoAdapter()->addCommand(cmd);
}
void addShapeLayer(KisDocument *doc, KisImageSP image) {
KisShapeLayerSP shapeLayer = new KisShapeLayer(doc->shapeController(), image.data(), "shape", OPACITY_OPAQUE_U8);
image->addNode(shapeLayer);
KoShapeFactoryBase *f1 = KoShapeRegistry::instance()->get("StarShape");
KoShapeFactoryBase *f2 = KoShapeRegistry::instance()->get("RectangleShape");
KoShape *shape1 = f1->createDefaultShape();
KoShape *shape2 = f2->createDefaultShape();
shape1->setPosition(QPointF(100,100));
shape2->setPosition(QPointF(200,200));
shapeLayer->addShape(shape1);
shapeLayer->addShape(shape2);
QApplication::processEvents();
}
bool checkLayersInitial(KisImageWSP image, int baseFuzzyness = 0) {
QString prefix = "initial_with_selection";
QString prefix2 = findNode(image->root(), "shape") ? "_with_shape" : "";
return checkLayers(image, prefix + prefix2, baseFuzzyness);
}
bool checkLayersInitialRootOnly(KisImageWSP image, int baseFuzzyness = 0) {
QString prefix = "initial_with_selection";
QString prefix2 = findNode(image->root(), "shape") ? "_with_shape" : "";
return checkLayers(image, prefix + prefix2, baseFuzzyness, false);
}
/**
* Checks the content of image's layers against the set of
* QImages stored in @p prefix subfolder
*/
bool checkLayers(KisImageWSP image, const QString &prefix, int baseFuzzyness = 0, bool recursive = true) {
QVector<QImage> images;
QVector<QString> names;
fillNamesImages(image->root(), image->bounds(), images, names, recursive);
bool valid = true;
const int stackSize = images.size();
for(int i = 0; i < stackSize; i++) {
if(!checkOneQImage(images[i], prefix, names[i], baseFuzzyness)) {
valid = false;
}
}
return valid;
}
/**
* Checks the content of one image's layer against the QImage
* stored in @p prefix subfolder
*/
bool checkOneLayer(KisImageWSP image, KisNodeSP node, const QString &prefix, int baseFuzzyness = 0) {
QVector<QImage> images;
QVector<QString> names;
fillNamesImages(node, image->bounds(), images, names);
return checkOneQImage(images.first(), prefix, names.first(), baseFuzzyness);
}
// add default bounds param
bool checkOneDevice(KisPaintDeviceSP device,
const QString &prefix,
const QString &name,
int baseFuzzyness = 0)
{
QImage image = device->convertToQImage(0);
return checkOneQImage(image, prefix, name, baseFuzzyness);
}
KisNodeSP findNode(KisNodeSP root, const QString &name) {
return TestUtil::findNode(root, name);
}
private:
bool checkOneQImage(const QImage &image,
const QString &prefix,
const QString &name,
int baseFuzzyness)
{
QString realName = prefix + "_" + name + ".png";
QString expectedName = prefix + "_" + name + "_expected.png";
bool valid = true;
QString fullPath = fetchDataFileLazy(m_directoryName + QDir::separator() +
prefix + QDir::separator() + realName);
if (fullPath.isEmpty()) {
// Try without the testname subdirectory
fullPath = fetchDataFileLazy(prefix + QDir::separator() +
realName);
}
if (fullPath.isEmpty()) {
// Try without the prefix subdirectory
fullPath = fetchDataFileLazy(m_directoryName + QDir::separator() +
realName);
}
QImage ref(fullPath);
QPoint temp;
int fuzzy = baseFuzzyness;
{
QStringList terms = name.split('_');
if(terms[0] == "root" ||
terms[0] == "blur1" ||
terms[0] == "shape") {
fuzzy++;
}
}
if(ref != image &&
!TestUtil::compareQImages(temp, ref, image, fuzzy)) {
- qDebug() << "--- Wrong image:" << realName;
+ dbgKrita << "--- Wrong image:" << realName;
valid = false;
image.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + realName);
ref.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + expectedName);
}
return valid;
}
void fillNamesImages(KisNodeSP node, const QRect &rc,
QVector<QImage> &images,
QVector<QString> &names,
bool recursive = true) {
while (node) {
if(node->paintDevice()) {
names.append(node->name() + "_paintDevice");
images.append(node->paintDevice()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if(node->original() && node->original() != node->paintDevice()) {
names.append(node->name() + "_original");
images.append(node->original()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if(node->projection() && node->projection() != node->paintDevice()) {
names.append(node->name() + "_projection");
images.append(node->projection()->
convertToQImage(0, rc.x(), rc.y(),
rc.width(), rc.height()));
}
if (recursive) {
fillNamesImages(node->firstChild(), rc, images, names);
}
node = node->nextSibling();
}
}
private:
QString m_directoryName;
};
}
#endif /* __QIMAGE_BASED_TEST_H */
diff --git a/krita/sdk/tests/stroke_testing_utils.cpp b/krita/sdk/tests/stroke_testing_utils.cpp
index 2f5d7cca4b6..8433309d24a 100644
--- a/krita/sdk/tests/stroke_testing_utils.cpp
+++ b/krita/sdk/tests/stroke_testing_utils.cpp
@@ -1,345 +1,345 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "stroke_testing_utils.h"
#include <QtTest>
#include <QDir>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include "kis_painter.h"
#include "kis_paintop_preset.h"
#include "KoPattern.h"
#include "kis_canvas_resource_provider.h"
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "testutil.h"
KisImageSP utils::createImage(KisUndoStore *undoStore, const QSize &imageSize) {
QRect imageRect(0,0,imageSize.width(),imageSize.height());
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "stroke test");
KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8);
KisPaintLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8);
image->lock();
image->addNode(paintLayer1);
image->addNode(paintLayer2);
image->addNode(paintLayer3);
image->addNode(paintLayer4);
image->addNode(paintLayer5);
image->unlock();
return image;
}
KoCanvasResourceManager* utils::createResourceManager(KisImageWSP image,
KisNodeSP node,
const QString &presetFileName)
{
KoCanvasResourceManager *manager = new KoCanvasResourceManager();
QVariant i;
i.setValue(KoColor(Qt::black, image->colorSpace()));
manager->setResource(KoCanvasResourceManager::ForegroundColor, i);
i.setValue(KoColor(Qt::white, image->colorSpace()));
manager->setResource(KoCanvasResourceManager::BackgroundColor, i);
i.setValue(static_cast<void*>(0));
manager->setResource(KisCanvasResourceProvider::CurrentPattern, i);
manager->setResource(KisCanvasResourceProvider::CurrentGradient, i);
manager->setResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration, i);
if(!node) {
node = image->root();
while(node && !dynamic_cast<KisPaintLayer*>(node.data())) {
node = node->firstChild();
}
Q_ASSERT(node && dynamic_cast<KisPaintLayer*>(node.data()));
}
i.setValue(node);
manager->setResource(KisCanvasResourceProvider::CurrentKritaNode, i);
KisPaintOpPresetSP preset;
if (!presetFileName.isEmpty()) {
QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName);
preset = new KisPaintOpPreset(fullFileName);
bool presetValid = preset->load();
Q_ASSERT(presetValid); Q_UNUSED(presetValid);
i.setValue(preset);
manager->setResource(KisCanvasResourceProvider::CurrentPaintOpPreset, i);
}
i.setValue(COMPOSITE_OVER);
manager->setResource(KisCanvasResourceProvider::CurrentCompositeOp, i);
i.setValue(false);
manager->setResource(KisCanvasResourceProvider::MirrorHorizontal, i);
i.setValue(false);
manager->setResource(KisCanvasResourceProvider::MirrorVertical, i);
i.setValue(1.0);
manager->setResource(KisCanvasResourceProvider::Opacity, i);
i.setValue(1.0);
manager->setResource(KisCanvasResourceProvider::HdrExposure, i);
i.setValue(QPoint());
manager->setResource(KisCanvasResourceProvider::MirrorAxesCenter, i);
return manager;
}
utils::StrokeTester::StrokeTester(const QString &name, const QSize &imageSize, const QString &presetFilename)
: m_name(name),
m_imageSize(imageSize),
m_presetFilename(presetFilename),
m_numIterations(1),
m_baseFuzziness(1)
{
}
utils::StrokeTester::~StrokeTester()
{
}
void utils::StrokeTester::setNumIterations(int value)
{
m_numIterations = value;
}
void utils::StrokeTester::setBaseFuzziness(int value)
{
m_baseFuzziness = value;
}
void utils::StrokeTester::test()
{
testOneStroke(false, false, false);
testOneStroke(false, true, false);
testOneStroke(true, false, false);
testOneStroke(true, true, false);
// The same but with updates (compare against projection)
testOneStroke(false, false, false, true);
testOneStroke(false, true, false, true);
testOneStroke(true, false, false, true);
testOneStroke(true, true, false, true);
// The same, but with an external layer
testOneStroke(false, false, true);
testOneStroke(false, true, true);
testOneStroke(true, false, true);
testOneStroke(true, true, true);
}
void utils::StrokeTester::benchmark()
{
// not cancelled, indirect painting, internal, no updates, no qimage
doStroke(false, true, false, false, false);
}
void utils::StrokeTester::testOneStroke(bool cancelled,
bool indirectPainting,
bool externalLayer,
bool testUpdates)
{
QString testName = formatTestName(m_name,
cancelled,
indirectPainting,
externalLayer);
- qDebug() << "Testcase:" << testName
+ dbgKrita << "Testcase:" << testName
<< "(comare against " << (testUpdates ? "projection" : "layer") << ")";
QImage resultImage;
resultImage = doStroke(cancelled, indirectPainting, externalLayer, testUpdates);
QImage refImage;
refImage.load(referenceFile(testName));
QPoint temp;
if(!TestUtil::compareQImages(temp, refImage, resultImage, m_baseFuzziness, m_baseFuzziness)) {
refImage.save(dumpReferenceFile(testName));
resultImage.save(resultFile(testName));
QFAIL("Images do not coincide");
}
}
QString utils::StrokeTester::formatTestName(const QString &baseName,
bool cancelled,
bool indirectPainting,
bool externalLayer)
{
QString result = baseName;
result += "_" + m_presetFilename;
result += indirectPainting ? "_indirect" : "_incremental";
result += cancelled ? "_cancelled" : "_finished";
result += externalLayer ? "_external" : "_internal";
return result;
}
QString utils::StrokeTester::referenceFile(const QString &testName)
{
QString path =
QString(FILES_DATA_DIR) + QDir::separator() +
m_name + QDir::separator();
path += testName;
path += ".png";
return path;
}
QString utils::StrokeTester::dumpReferenceFile(const QString &testName)
{
QString path = QString(FILES_OUTPUT_DIR) + QDir::separator();
path += testName;
path += "_expected";
path += ".png";
return path;
}
QString utils::StrokeTester::resultFile(const QString &testName)
{
QString path = QString(FILES_OUTPUT_DIR) + QDir::separator();
path += testName;
path += ".png";
return path;
}
QImage utils::StrokeTester::doStroke(bool cancelled,
bool indirectPainting,
bool externalLayer,
bool testUpdates,
bool needQImage)
{
KisImageSP image = utils::createImage(0, m_imageSize);
KoCanvasResourceManager *manager = utils::createResourceManager(image, 0, m_presetFilename);
KisNodeSP currentNode;
for (int i = 0; i < m_numIterations; i++) {
modifyResourceManager(manager, image, i);
KisPainter *painter = new KisPainter();
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
image->rootLayer()->firstChild(),
image->postExecutionUndoAdapter(),
manager);
if(externalLayer) {
KisNodeSP externalNode = new KisPaintLayer(0, "extlyr", OPACITY_OPAQUE_U8, image->colorSpace());
resources->setCurrentNode(externalNode);
Q_ASSERT(resources->currentNode() == externalNode);
}
initImage(image, resources->currentNode(), i);
KisStrokeStrategy *stroke = createStroke(indirectPainting, resources, painter, image);
m_strokeId = image->startStroke(stroke);
addPaintingJobs(image, resources, painter, i);
if(!cancelled) {
image->endStroke(m_strokeId);
}
else {
image->cancelStroke(m_strokeId);
}
image->waitForDone();
currentNode = resources->currentNode();
}
QImage resultImage;
if(needQImage) {
KisPaintDeviceSP device = testUpdates ?
image->projection() :
currentNode->paintDevice();
resultImage = device->convertToQImage(0, 0, 0, image->width(), image->height());
}
image = 0;
delete manager;
return resultImage;
}
void utils::StrokeTester::modifyResourceManager(KoCanvasResourceManager *manager,
KisImageWSP image, int iteration)
{
Q_UNUSED(iteration);
modifyResourceManager(manager, image);
}
void utils::StrokeTester::modifyResourceManager(KoCanvasResourceManager *manager,
KisImageWSP image)
{
Q_UNUSED(manager);
Q_UNUSED(image);
}
void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode, int iteration)
{
Q_UNUSED(iteration);
initImage(image, activeNode);
}
void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode)
{
Q_UNUSED(image);
Q_UNUSED(activeNode);
}
void utils::StrokeTester::addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources,
KisPainter *painter, int iteration)
{
Q_UNUSED(iteration);
addPaintingJobs(image, resources, painter);
}
void utils::StrokeTester::addPaintingJobs(KisImageWSP image,
KisResourcesSnapshotSP resources,
KisPainter *painter)
{
Q_UNUSED(image);
Q_UNUSED(resources);
Q_UNUSED(painter);
}
diff --git a/krita/sdk/tests/testutil.h b/krita/sdk/tests/testutil.h
index d0c96562695..4ddb3472e46 100644
--- a/krita/sdk/tests/testutil.h
+++ b/krita/sdk/tests/testutil.h
@@ -1,604 +1,604 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TEST_UTIL
#define TEST_UTIL
#include <QList>
#include <QTime>
#include <QDir>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoProgressProxy.h>
#include <kis_paint_device.h>
#include <kis_node.h>
#include <kis_undo_adapter.h>
#include "kis_node_graph_listener.h"
#include "kis_iterator_ng.h"
#include "kis_image.h"
#ifndef FILES_DATA_DIR
#define FILES_DATA_DIR "."
#endif
#ifndef FILES_DEFAULT_DATA_DIR
#define FILES_DEFAULT_DATA_DIR "."
#endif
/**
* Routines that are useful for writing efficient tests
*/
namespace TestUtil
{
inline KisNodeSP findNode(KisNodeSP root, const QString &name) {
if(root->name() == name) return root;
KisNodeSP child = root->firstChild();
while (child) {
if((root = findNode(child, name))) return root;
child = child->nextSibling();
}
return 0;
}
#include <QProcessEnvironment>
inline QString fetchExternalDataFileName(const QString relativeFileName)
{
static QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
static QString unittestsDataDirPath = "KRITA_UNITTESTS_DATA_DIR";
QString path;
if (!env.contains(unittestsDataDirPath)) {
- qWarning() << "Environment variable" << unittestsDataDirPath << "is not set";
+ warnKrita << "Environment variable" << unittestsDataDirPath << "is not set";
return QString();
} else {
path = env.value(unittestsDataDirPath, "");
}
QString filename =
path +
QDir::separator() +
relativeFileName;
return filename;
}
inline QString fetchDataFileLazy(const QString relativeFileName, bool externalTest = false)
{
if (externalTest) {
return fetchExternalDataFileName(relativeFileName);
} else {
QString filename =
QString(FILES_DATA_DIR) +
QDir::separator() +
relativeFileName;
if (QFileInfo(filename).exists()) {
return filename;
}
filename =
QString(FILES_DEFAULT_DATA_DIR) +
QDir::separator() +
relativeFileName;
if (QFileInfo(filename).exists()) {
return filename;
}
}
return QString();
}
inline void dumpNodeStack(KisNodeSP node, QString prefix = QString("\t"))
{
- qDebug() << node->name();
+ dbgKrita << node->name();
KisNodeSP child = node->firstChild();
while (child) {
if (child->childCount() > 0) {
dumpNodeStack(child, prefix + "\t");
} else {
- qDebug() << prefix << child->name();
+ dbgKrita << prefix << child->name();
}
child = child->nextSibling();
}
}
class TestProgressBar : public KoProgressProxy {
public:
TestProgressBar()
: m_min(0), m_max(0), m_value(0)
{}
int maximum() const {
return m_max;
}
void setValue(int value) {
m_value = value;
}
void setRange(int min, int max) {
m_min = min;
m_max = max;
}
void setFormat(const QString &format) {
m_format = format;
}
int min() { return m_min; }
int max() { return m_max; }
int value() { return m_value; }
QString format() { return m_format; }
private:
int m_min;
int m_max;
int m_value;
QString m_format;
};
inline bool compareQImages(QPoint & pt, const QImage & image1, const QImage & image2, int fuzzy = 0, int fuzzyAlpha = 0, int maxNumFailingPixels = 0)
{
// QTime t;
// t.start();
const int w1 = image1.width();
const int h1 = image1.height();
const int w2 = image2.width();
const int h2 = image2.height();
const int bytesPerLine = image1.bytesPerLine();
if (w1 != w2 || h1 != h2) {
pt.setX(-1);
pt.setY(-1);
- qDebug() << "Images have different sizes" << image1.size() << image2.size();
+ dbgKrita << "Images have different sizes" << image1.size() << image2.size();
return false;
}
int numFailingPixels = 0;
for (int y = 0; y < h1; ++y) {
const QRgb * const firstLine = reinterpret_cast<const QRgb *>(image2.scanLine(y));
const QRgb * const secondLine = reinterpret_cast<const QRgb *>(image1.scanLine(y));
if (memcmp(firstLine, secondLine, bytesPerLine) != 0) {
for (int x = 0; x < w1; ++x) {
const QRgb a = firstLine[x];
const QRgb b = secondLine[x];
const bool same = qAbs(qRed(a) - qRed(b)) <= fuzzy
&& qAbs(qGreen(a) - qGreen(b)) <= fuzzy
&& qAbs(qBlue(a) - qBlue(b)) <= fuzzy;
const bool sameAlpha = qAlpha(a) - qAlpha(b) <= fuzzyAlpha;
const bool bothTransparent = sameAlpha && qAlpha(a)==0;
if (!bothTransparent && (!same || !sameAlpha)) {
pt.setX(x);
pt.setY(y);
numFailingPixels++;
- qDebug() << " Different at" << pt
+ dbgKrita << " Different at" << pt
<< "source" << qRed(a) << qGreen(a) << qBlue(a) << qAlpha(a)
<< "dest" << qRed(b) << qGreen(b) << qBlue(b) << qAlpha(b)
<< "fuzzy" << fuzzy
<< "fuzzyAlpha" << fuzzyAlpha
<< "(" << numFailingPixels << "of" << maxNumFailingPixels << "allowed )";
if (numFailingPixels > maxNumFailingPixels) {
return false;
}
}
}
}
}
- // qDebug() << "compareQImages time elapsed:" << t.elapsed();
- // qDebug() << "Images are identical";
+ // dbgKrita << "compareQImages time elapsed:" << t.elapsed();
+ // dbgKrita << "Images are identical";
return true;
}
inline bool comparePaintDevices(QPoint & pt, const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2)
{
// QTime t;
// t.start();
QRect rc1 = dev1->exactBounds();
QRect rc2 = dev2->exactBounds();
if (rc1 != rc2) {
pt.setX(-1);
pt.setY(-1);
}
KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width());
KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width());
int pixelSize = dev1->pixelSize();
for (int y = 0; y < rc1.height(); ++y) {
do {
if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0)
return false;
} while (iter1->nextPixel() && iter2->nextPixel());
iter1->nextRow();
iter2->nextRow();
}
- // qDebug() << "comparePaintDevices time elapsed:" << t.elapsed();
+ // dbgKrita << "comparePaintDevices time elapsed:" << t.elapsed();
return true;
}
template <typename channel_type>
inline bool comparePaintDevicesClever(const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2, channel_type alphaThreshold = 0)
{
QRect rc1 = dev1->exactBounds();
QRect rc2 = dev2->exactBounds();
if (rc1 != rc2) {
- qDebug() << "Devices have different size" << ppVar(rc1) << ppVar(rc2);
+ dbgKrita << "Devices have different size" << ppVar(rc1) << ppVar(rc2);
return false;
}
KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width());
KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width());
int pixelSize = dev1->pixelSize();
for (int y = 0; y < rc1.height(); ++y) {
do {
if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0) {
const channel_type* p1 = reinterpret_cast<const channel_type*>(iter1->oldRawData());
const channel_type* p2 = reinterpret_cast<const channel_type*>(iter2->oldRawData());
if (p1[3] < alphaThreshold && p2[3] < alphaThreshold) continue;
- qDebug() << "Failed compare paint devices:" << iter1->x() << iter1->y();
- qDebug() << "src:" << p1[0] << p1[1] << p1[2] << p1[3];
- qDebug() << "dst:" << p2[0] << p2[1] << p2[2] << p2[3];
+ dbgKrita << "Failed compare paint devices:" << iter1->x() << iter1->y();
+ dbgKrita << "src:" << p1[0] << p1[1] << p1[2] << p1[3];
+ dbgKrita << "dst:" << p2[0] << p2[1] << p2[2] << p2[3];
return false;
}
} while (iter1->nextPixel() && iter2->nextPixel());
iter1->nextRow();
iter2->nextRow();
}
return true;
}
#ifdef FILES_OUTPUT_DIR
inline bool checkQImageImpl(bool externalTest,
const QImage &srcImage, const QString &testName,
const QString &prefix, const QString &name,
int fuzzy, int fuzzyAlpha, int maxNumFailingPixels)
{
QImage image = srcImage.convertToFormat(QImage::Format_ARGB32);
if (fuzzyAlpha == -1) {
fuzzyAlpha = fuzzy;
}
QString filename(prefix + "_" + name + ".png");
QString dumpName(prefix + "_" + name + "_expected.png");
const QString standardPath =
testName + QDir::separator() +
prefix + QDir::separator() + filename;
QString fullPath = fetchDataFileLazy(standardPath, externalTest);
if (fullPath.isEmpty() || !QFileInfo(fullPath).exists()) {
// Try without the testname subdirectory
fullPath = fetchDataFileLazy(prefix + QDir::separator() +
filename,
externalTest);
}
if (fullPath.isEmpty() || !QFileInfo(fullPath).exists()) {
// Try without the prefix subdirectory
fullPath = fetchDataFileLazy(testName + QDir::separator() +
filename,
externalTest);
}
bool canSkipExternalTest = fullPath.isEmpty() && externalTest;
QImage ref(fullPath);
bool valid = true;
QPoint t;
if(!compareQImages(t, image, ref, fuzzy, fuzzyAlpha, maxNumFailingPixels)) {
bool saveStandardResults = true;
if (canSkipExternalTest) {
static QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
static QString writeUnittestsVar = "KRITA_WRITE_UNITTESTS";
int writeUnittests = env.value(writeUnittestsVar, "0").toInt();
if (writeUnittests) {
QString path = fetchExternalDataFileName(standardPath);
QFileInfo pathInfo(path);
QDir directory;
directory.mkpath(pathInfo.path());
- qDebug() << "--- Saving reference image:" << name << path;
+ dbgKrita << "--- Saving reference image:" << name << path;
image.save(path);
saveStandardResults = false;
} else {
- qDebug() << "--- External image not found. Skipping..." << name;
+ dbgKrita << "--- External image not found. Skipping..." << name;
}
} else {
- qDebug() << "--- Wrong image:" << name;
+ dbgKrita << "--- Wrong image:" << name;
valid = false;
}
if (saveStandardResults) {
image.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + filename);
ref.save(QString(FILES_OUTPUT_DIR) + QDir::separator() + dumpName);
}
}
return valid;
}
inline bool checkQImage(const QImage &image, const QString &testName,
const QString &prefix, const QString &name,
int fuzzy = 0, int fuzzyAlpha = -1, int maxNumFailingPixels = 0)
{
return checkQImageImpl(false, image, testName,
prefix, name,
fuzzy, fuzzyAlpha, maxNumFailingPixels);
}
inline bool checkQImageExternal(const QImage &image, const QString &testName,
const QString &prefix, const QString &name,
int fuzzy = 0, int fuzzyAlpha = -1, int maxNumFailingPixels = 0)
{
return checkQImageImpl(true, image, testName,
prefix, name,
fuzzy, fuzzyAlpha, maxNumFailingPixels);
}
struct ExternalImageChecker
{
ExternalImageChecker(const QString &prefix, const QString &testName)
: m_prefix(prefix),
m_testName(testName),
m_success(true)
{
}
bool testPassed() const {
return m_success;
}
inline bool checkDevice(KisPaintDeviceSP device, KisImageSP image, const QString &caseName) {
bool result =
checkQImageExternal(device->convertToQImage(0, image->bounds()),
m_testName,
m_prefix,
caseName, 1, 1, 100);
m_success &= result;
return result;
}
inline bool checkImage(KisImageSP image, const QString &testName) {
bool result = checkDevice(image->projection(), image, testName);
m_success &= result;
return result;
}
private:
QString m_prefix;
QString m_testName;
bool m_success;
};
#endif
inline quint8 alphaDevicePixel(KisPaintDeviceSP dev, qint32 x, qint32 y)
{
KisHLineConstIteratorSP iter = dev->createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->oldRawData();
return *pix;
}
inline void alphaDeviceSetPixel(KisPaintDeviceSP dev, qint32 x, qint32 y, quint8 s)
{
KisHLineIteratorSP iter = dev->createHLineIteratorNG(x, y, 1);
quint8 *pix = iter->rawData();
*pix = s;
}
inline bool checkAlphaDeviceFilledWithPixel(KisPaintDeviceSP dev, const QRect &rc, quint8 expected)
{
KisHLineIteratorSP it = dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
for (int y = rc.y(); y < rc.y() + rc.height(); y++) {
for (int x = rc.x(); x < rc.x() + rc.width(); x++) {
if(*((quint8*)it->rawData()) != expected) {
- qCritical() << "At point:" << x << y;
- qCritical() << "Expected pixel:" << expected;
- qCritical() << "Actual pixel: " << *((quint8*)it->rawData());
+ errKrita << "At point:" << x << y;
+ errKrita << "Expected pixel:" << expected;
+ errKrita << "Actual pixel: " << *((quint8*)it->rawData());
return false;
}
it->nextPixel();
}
it->nextRow();
}
return true;
}
class TestNode : public KisNode
{
Q_OBJECT
public:
KisNodeSP clone() const;
bool allowAsChild(KisNodeSP) const;
const KoColorSpace * colorSpace() const;
const KoCompositeOp * compositeOp() const;
};
class TestGraphListener : public KisNodeGraphListener
{
public:
virtual void aboutToAddANode(KisNode *parent, int index) {
KisNodeGraphListener::aboutToAddANode(parent, index);
beforeInsertRow = true;
}
virtual void nodeHasBeenAdded(KisNode *parent, int index) {
KisNodeGraphListener::nodeHasBeenAdded(parent, index);
afterInsertRow = true;
}
virtual void aboutToRemoveANode(KisNode *parent, int index) {
KisNodeGraphListener::aboutToRemoveANode(parent, index);
beforeRemoveRow = true;
}
virtual void nodeHasBeenRemoved(KisNode *parent, int index) {
KisNodeGraphListener::nodeHasBeenRemoved(parent, index);
afterRemoveRow = true;
}
virtual void aboutToMoveNode(KisNode *parent, int oldIndex, int newIndex) {
KisNodeGraphListener::aboutToMoveNode(parent, oldIndex, newIndex);
beforeMove = true;
}
virtual void nodeHasBeenMoved(KisNode *parent, int oldIndex, int newIndex) {
KisNodeGraphListener::nodeHasBeenMoved(parent, oldIndex, newIndex);
afterMove = true;
}
bool beforeInsertRow;
bool afterInsertRow;
bool beforeRemoveRow;
bool afterRemoveRow;
bool beforeMove;
bool afterMove;
void resetBools() {
beforeRemoveRow = false;
afterRemoveRow = false;
beforeInsertRow = false;
afterInsertRow = false;
beforeMove = false;
afterMove = false;
}
};
}
#include <kis_paint_layer.h>
#include <kis_image.h>
#include "kis_undo_stores.h"
namespace TestUtil {
struct MaskParent
{
MaskParent(const QRect &_imageRect = QRect(0,0,512,512))
: imageRect(_imageRect) {
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
undoStore = new KisSurrogateUndoStore();
image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "test image");
layer = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8);
image->addNode(layer);
}
KisSurrogateUndoStore *undoStore;
const QRect imageRect;
KisImageSP image;
KisPaintLayerSP layer;
};
}
namespace TestUtil {
class MeasureAvgPortion
{
public:
MeasureAvgPortion(int period)
: m_period(period),
m_val(0),
m_total(0),
m_cycles(0)
{
}
~MeasureAvgPortion() {
printValues(true);
}
void addVal(int x) {
m_val += x;
}
void addTotal(int x) {
m_total += x;
m_cycles++;
printValues();
}
private:
void printValues(bool force = false) {
if (m_cycles > m_period || force) {
- qDebug() << "Val / Total:" << qreal(m_val) / qreal(m_total);
- qDebug() << "Avg. Val: " << qreal(m_val) / m_cycles;
- qDebug() << "Avg. Total: " << qreal(m_total) / m_cycles;
- qDebug() << ppVar(m_val) << ppVar(m_total) << ppVar(m_cycles);
+ dbgKrita << "Val / Total:" << qreal(m_val) / qreal(m_total);
+ dbgKrita << "Avg. Val: " << qreal(m_val) / m_cycles;
+ dbgKrita << "Avg. Total: " << qreal(m_total) / m_cycles;
+ dbgKrita << ppVar(m_val) << ppVar(m_total) << ppVar(m_cycles);
m_val = 0;
m_total = 0;
m_cycles = 0;
}
}
private:
int m_period;
qint64 m_val;
qint64 m_total;
qint64 m_cycles;
};
}
#endif
diff --git a/krita/sketch/CurveEditorItem.cpp b/krita/sketch/CurveEditorItem.cpp
index 907c917fdf3..ad75c1e0e28 100644
--- a/krita/sketch/CurveEditorItem.cpp
+++ b/krita/sketch/CurveEditorItem.cpp
@@ -1,139 +1,139 @@
/* This file is part of the KDE project
* Copyright 2014 Dan Leinir Turthra Jensen <admin@leinir.dk>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "CurveEditorItem.h"
#include "kis_curve_widget.h"
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
-#include <QDebug>
+#include <kis_debug.h>
class CurveEditorItem::Private
{
public:
Private(CurveEditorItem* qq)
: q(qq)
{
curveWidget = new KisCurveWidget();
}
~Private()
{
delete curveWidget;
}
void repaint();
CurveEditorItem* q;
KisCurveWidget* curveWidget;
QImage contents;
};
CurveEditorItem::CurveEditorItem(QDeclarativeItem* parent)
: QDeclarativeItem(parent)
, d(new Private(this))
{
setFlag( QGraphicsItem::ItemHasNoContents, false );
setAcceptedMouseButtons( Qt::LeftButton | Qt::RightButton | Qt::MidButton );
connect(d->curveWidget, SIGNAL(pointSelectedChanged()), SIGNAL(pointSelectedChanged()));
connect(d->curveWidget, SIGNAL(modified()), SIGNAL(curveChanged()));
qRegisterMetaType<KisCubicCurve>();
}
CurveEditorItem::~CurveEditorItem()
{
delete d;
}
void CurveEditorItem::paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*)
{
p->drawImage(boundingRect(), d->contents);
}
KisCubicCurve CurveEditorItem::curve() const
{
return d->curveWidget->curve();
}
void CurveEditorItem::setCurve(KisCubicCurve curve)
{
d->curveWidget->setCurve(curve);
emit curveChanged();
}
bool CurveEditorItem::pointSelected() const
{
return d->curveWidget->pointSelected();
}
void CurveEditorItem::deleteSelectedPoint()
{
if(pointSelected()) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
d->curveWidget->keyPressEvent(event);
d->repaint();
}
}
void CurveEditorItem::Private::repaint()
{
curveWidget->resize(q->boundingRect().size().toSize());
contents = QImage(q->boundingRect().size().toSize(), QImage::Format_ARGB32_Premultiplied);
contents.fill(Qt::blue);
curveWidget->render(&contents);
q->update();
}
void CurveEditorItem::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
d->repaint();
QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
}
void CurveEditorItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
QMouseEvent* mouseEvent = new QMouseEvent(event->type(), event->pos().toPoint(), event->button(), event->buttons(), event->modifiers());
d->curveWidget->mousePressEvent(mouseEvent);
if(mouseEvent->isAccepted()) {
event->accept();
}
d->repaint();
}
void CurveEditorItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
QMouseEvent* mouseEvent = new QMouseEvent(event->type(), event->pos().toPoint(), event->button(), event->buttons(), event->modifiers());
d->curveWidget->mouseMoveEvent(mouseEvent);
if(mouseEvent->isAccepted()) {
event->accept();
}
d->repaint();
}
void CurveEditorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QMouseEvent* mouseEvent = new QMouseEvent(event->type(), event->pos().toPoint(), event->button(), event->buttons(), event->modifiers());
d->curveWidget->mouseReleaseEvent(mouseEvent);
if(mouseEvent->isAccepted()) {
event->accept();
}
d->repaint();
}
diff --git a/krita/sketch/KisSelectionExtras.cpp b/krita/sketch/KisSelectionExtras.cpp
index a63783c826a..d873183dc7e 100644
--- a/krita/sketch/KisSelectionExtras.cpp
+++ b/krita/sketch/KisSelectionExtras.cpp
@@ -1,64 +1,64 @@
/* This file is part of the KDE project
* Copyright (C) 2013 Camilla Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisSelectionExtras.h"
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kis_selection_filters.h>
#include <operations/kis_filter_selection_operation.h>
#include "KisSketchView.h"
KisSelectionExtras::KisSelectionExtras(KisViewManager *view)
: m_view(view)
{
}
KisSelectionExtras::~KisSelectionExtras()
{
}
void KisSelectionExtras::grow(qint32 xradius, qint32 yradius)
{
KisSelectionFilter *filter = new KisGrowSelectionFilter(xradius, yradius);
KisFilterSelectionOperation opr("grow-oper");
opr.runFilter(filter, m_view, KisOperationConfiguration());
}
void KisSelectionExtras::shrink(qint32 xradius, qint32 yradius, bool edge_lock)
{
KisSelectionFilter *filter = new KisShrinkSelectionFilter(xradius, yradius, edge_lock);
KisFilterSelectionOperation opr("shrink-oper");
opr.runFilter(filter, m_view, KisOperationConfiguration());
}
void KisSelectionExtras::border(qint32 xradius, qint32 yradius)
{
KisSelectionFilter *filter = new KisBorderSelectionFilter(xradius, yradius);
KisFilterSelectionOperation opr("border-oper");
opr.runFilter(filter, m_view, KisOperationConfiguration());
}
void KisSelectionExtras::feather(qint32 radius)
{
KisSelectionFilter *filter = new KisFeatherSelectionFilter(radius);
KisFilterSelectionOperation opr("feather-oper");
opr.runFilter(filter, m_view, KisOperationConfiguration());
}
diff --git a/krita/sketch/KisSketchView.cpp b/krita/sketch/KisSketchView.cpp
index 1e98a2bbf9d..a7bce949a8f 100644
--- a/krita/sketch/KisSketchView.cpp
+++ b/krita/sketch/KisSketchView.cpp
@@ -1,706 +1,706 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Boudewijn Rempt <boud@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisSketchView.h"
#include <QTimer>
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QClipboard>
#include <QGraphicsSceneMouseEvent>
#include <QMouseEvent>
#include <QScrollBar>
#include <QHoverEvent>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kactioncollection.h>
#include <KoZoomHandler.h>
#include <KoZoomController.h>
#include <KoToolProxy.h>
#include <KoCanvasController.h>
#include <KisImportExportManager.h>
#include <KoShapeController.h>
#include <KoDocumentResourceManager.h>
#include <KoCanvasResourceManager.h>
#include <KoToolManager.h>
#include <KoGridData.h>
#include <kundo2stack.h>
#include "ProgressProxy.h"
#include <kis_factory2.h>
#include "kis_painter.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "KisDocument.h"
#include "kis_canvas2.h"
#include <kis_canvas_controller.h>
#include <kis_qpainter_canvas.h>
#include "kis_config.h"
#include <KisView.h>
#include "KisViewManager.h"
#include "kis_image.h"
#include <kis_image_signal_router.h>
#include "kis_clipboard.h"
#include <input/kis_input_manager.h>
#include <input/kis_tablet_event.h>
#include <kis_canvas_resource_provider.h>
#include <kis_zoom_manager.h>
#include <kis_selection_manager.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_qpainter_canvas.h>
#include <KisPart.h>
#include <kis_canvas_decoration.h>
#include <kis_tool_freehand.h>
#include "KisSelectionExtras.h"
#include "Settings.h"
#include "DocumentManager.h"
#include "SketchDeclarativeView.h"
#include "krita/gemini/ViewModeSwitchEvent.h"
class KisSketchView::Private
{
public:
Private( KisSketchView* qq)
: q(qq)
, actionCollection(0)
, doc(0)
, viewManager(0)
, view(0)
, canvas(0)
, canvasWidget(0)
, selectionExtras(0)
, undoAction(0)
, redoAction(0)
, tabletEventCount(0)
{ }
~Private() {
delete selectionExtras;
}
void imageUpdated(const QRect &updated);
void documentOffsetMoved();
void zoomChanged();
void resetDocumentPosition();
void removeNodeAsync(KisNodeSP removedNode);
KisSketchView* q;
KActionCollection *actionCollection;
QPointer<KisDocument> doc;
QPointer<KisViewManager> viewManager;
QPointer<KisView> view;
QPointer<KisCanvas2> canvas;
KUndo2Stack* undoStack;
QWidget *canvasWidget;
QString file;
KisSelectionExtras *selectionExtras;
QTimer *timer;
QTimer *loadedTimer;
QTimer *savedTimer;
QAction* undoAction;
QAction* redoAction;
unsigned char tabletEventCount;
};
KisSketchView::KisSketchView(QDeclarativeItem* parent)
: QDeclarativeItem(parent)
, d(new Private(this))
{
// this is just an interaction overlay, the contents are painted on the sceneview background
setFlag(QGraphicsItem::ItemHasNoContents, true);
setAcceptTouchEvents(true);
setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
setAcceptHoverEvents(true);
d->actionCollection = new KActionCollection(this, KComponentData(KisFactory::aboutData()));
d->viewManager = new KisViewManager(qApp->activeWindow(), d->actionCollection);
grabGesture(Qt::PanGesture);
//grabGesture(Qt::PinchGesture);
KoZoomMode::setMinimumZoom(0.1);
KoZoomMode::setMaximumZoom(16.0);
d->timer = new QTimer(this);
d->timer->setSingleShot(true);
connect(d->timer, SIGNAL(timeout()), this, SLOT(resetDocumentPosition()));
d->loadedTimer = new QTimer(this);
d->loadedTimer->setSingleShot(true);
d->loadedTimer->setInterval(100);
connect(d->loadedTimer, SIGNAL(timeout()), SIGNAL(loadingFinished()));
d->savedTimer = new QTimer(this);
d->savedTimer->setSingleShot(true);
d->savedTimer->setInterval(100);
connect(d->savedTimer, SIGNAL(timeout()), SIGNAL(savingFinished()));
connect(DocumentManager::instance(), SIGNAL(aboutToDeleteDocument()), SLOT(documentAboutToBeDeleted()));
connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(documentChanged()));
connect(DocumentManager::instance()->progressProxy(), SIGNAL(valueChanged(int)), SIGNAL(progress(int)));
connect(DocumentManager::instance(), SIGNAL(documentSaved()), d->savedTimer, SLOT(start()));
if (DocumentManager::instance()->document()) {
documentChanged();
}
}
KisSketchView::~KisSketchView()
{
if (d->doc) {
DocumentManager::instance()->closeDocument();
}
if (d->canvasWidget) {
SketchDeclarativeView *v = qobject_cast<SketchDeclarativeView*>(scene()->views().at(0));
if (v) {
v->setCanvasWidget(0);
v->setDrawCanvas(false);
}
}
delete d;
}
QObject* KisSketchView::selectionManager() const
{
if (!d->viewManager)
return 0;
return d->viewManager->selectionManager();
}
QObject* KisSketchView::selectionExtras() const
{
if (!d->selectionExtras) {
d->selectionExtras = new KisSelectionExtras(d->viewManager);
}
return d->selectionExtras;
}
QObject* KisSketchView::doc() const
{
return d->doc;
}
QObject* KisSketchView::view() const
{
return d->viewManager;
}
QString KisSketchView::file() const
{
return d->file;
}
QString KisSketchView::fileTitle() const
{
QFileInfo file(d->file);
return file.fileName();
}
bool KisSketchView::isModified() const
{
if(d->doc)
return d->doc->isModified();
return false;
}
void KisSketchView::setFile(const QString& file)
{
if (!file.isEmpty() && file != d->file) {
d->file = file;
emit fileChanged();
if (!file.startsWith("temp://")) {
DocumentManager::instance()->openDocument(file);
}
}
}
void KisSketchView::componentComplete()
{
}
bool KisSketchView::canUndo() const
{
if (d->undoAction)
return d->undoAction->isEnabled();
return false;
}
bool KisSketchView::canRedo() const
{
if (d->redoAction)
return d->redoAction->isEnabled();
return false;
}
int KisSketchView::imageHeight() const
{
if (d->doc)
return d->doc->image()->height();
return 0;
}
int KisSketchView::imageWidth() const
{
if (d->doc)
return d->doc->image()->width();
return 0;
}
void KisSketchView::undo()
{
d->undoAction->trigger();
}
void KisSketchView::redo()
{
d->redoAction->trigger();
}
void KisSketchView::zoomIn()
{
d->viewManager->actionCollection()->action("zoom_in")->trigger();
}
void KisSketchView::zoomOut()
{
d->viewManager->actionCollection()->action("zoom_out")->trigger();
}
void KisSketchView::save()
{
DocumentManager::instance()->save();
}
void KisSketchView::saveAs(const QString& fileName, const QString& mimeType)
{
DocumentManager::instance()->saveAs(fileName, mimeType);
}
void KisSketchView::documentAboutToBeDeleted()
{
if (d->undoAction)
d->undoAction->disconnect(this);
if (d->redoAction)
d->redoAction->disconnect(this);
delete d->view;
d->view = 0;
emit viewChanged();
d->canvas = 0;
d->canvasWidget = 0;
}
void KisSketchView::documentChanged()
{
d->doc = DocumentManager::instance()->document();
if (!d->doc) return;
if (!d->viewManager) return;
if (!d->viewManager->canvasBase()) return;
connect(d->doc, SIGNAL(modified(bool)), SIGNAL(modifiedChanged()));
QPointer<KisView> view = qobject_cast<KisView*>(KisPart::instance()->createView(d->doc,
d->viewManager->resourceProvider()->resourceManager(),
d->viewManager->actionCollection(),
QApplication::activeWindow()));
d->view = view;
d->view->setShowFloatingMessage(false);
KisCanvasController *controller = dynamic_cast<KisCanvasController*>(d->viewManager->canvasBase()->canvasController());
connect(d->view, SIGNAL(floatingMessageRequested(QString,QString)), this, SIGNAL(floatingMessageRequested(QString,QString)));
controller->setGeometry(x(), y(), width(), height());
d->view->hide();
d->canvas = d->view->canvasBase();
d->undoStack = d->doc->undoStack();
d->undoAction = d->viewManager->actionCollection()->action("edit_undo");
connect(d->undoAction, SIGNAL(changed()), this, SIGNAL(canUndoChanged()));
d->redoAction = d->viewManager->actionCollection()->action("edit_redo");
connect(d->redoAction, SIGNAL(changed()), this, SIGNAL(canRedoChanged()));
KoToolManager::instance()->switchToolRequested( "KritaShape/KisToolBrush" );
d->canvasWidget = d->canvas->canvasWidget();
connect(d->doc->image(), SIGNAL(sigImageUpdated(QRect)), SLOT(imageUpdated(QRect)));
connect(controller->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved()));
connect(d->view->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), SLOT(zoomChanged()));
connect(d->canvas, SIGNAL(updateCanvasRequested(QRect)), SLOT(imageUpdated(QRect)));
connect(d->doc->image()->signalRouter(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(removeNodeAsync(KisNodeSP)));
connect(d->doc->image()->signalRouter(), SIGNAL(sigSizeChanged(QPointF,QPointF)), SIGNAL(imageSizeChanged()));
if(scene()) {
SketchDeclarativeView *v = qobject_cast<SketchDeclarativeView*>(scene()->views().at(0));
if (v) {
v->setCanvasWidget(d->canvasWidget);
v->setDrawCanvas(true);
}
}
d->imageUpdated(d->canvas->image()->bounds());
static_cast<KoZoomHandler*>(d->canvas->viewConverter())->setResolution(d->doc->image()->xRes(), d->doc->image()->yRes());
d->view->zoomController()->setZoomMode(KoZoomMode::ZOOM_PAGE);
controller->setScrollBarValue(QPoint(0, 0));
controller->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
controller->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
geometryChanged(QRectF(x(), y(), width(), height()), QRectF());
d->loadedTimer->start(100);
d->viewManager->actionCollection()->action("zoom_to_100pct")->trigger();
d->resetDocumentPosition();
emit viewChanged();
}
bool KisSketchView::event( QEvent* event )
{
if (!d->viewManager) return false;
if (!d->viewManager->canvasBase()) return false;
KisCanvasController *controller = dynamic_cast<KisCanvasController*>(d->viewManager->canvasBase()->canvasController());
if (!controller) return false;
switch(static_cast<int>(event->type())) {
case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: {
ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
if (d->view && d->viewManager && d->viewManager->canvasBase()) {
controller->setFocus();
qApp->processEvents();
KisCanvasResourceProvider* provider = d->view->resourceProvider();
syncObject->backgroundColor = provider->bgColor();
syncObject->foregroundColor = provider->fgColor();
syncObject->exposure = provider->HDRExposure();
syncObject->gamma = provider->HDRGamma();
syncObject->compositeOp = provider->currentCompositeOp();
syncObject->pattern = provider->currentPattern();
syncObject->gradient = provider->currentGradient();
syncObject->node = provider->currentNode();
syncObject->paintOp = provider->currentPreset();
syncObject->opacity = provider->opacity();
syncObject->globalAlphaLock = provider->globalAlphaLock();
syncObject->documentOffset = controller->scrollBarValue();
syncObject->zoomLevel = d->view->zoomController()->zoomAction()->effectiveZoom();
syncObject->rotationAngle = d->view->canvasBase()->rotationAngle();
syncObject->activeToolId = KoToolManager::instance()->activeToolId();
syncObject->gridData = &d->view->document()->gridData();
syncObject->mirrorHorizontal = provider->mirrorHorizontal();
syncObject->mirrorVertical = provider->mirrorVertical();
syncObject->mirrorAxesCenter = provider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF();
KisToolFreehand* tool = qobject_cast<KisToolFreehand*>(KoToolManager::instance()->toolById(d->view->canvasBase(), syncObject->activeToolId));
if(tool) {
syncObject->smoothingOptions = tool->smoothingOptions();
}
syncObject->initialized = true;
}
return true;
}
case ViewModeSwitchEvent::SwitchedToSketchModeEvent: {
ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
if (d->view && syncObject->initialized) {
controller->setFocus();
qApp->processEvents();
KisToolFreehand* tool = qobject_cast<KisToolFreehand*>(KoToolManager::instance()->toolById(d->view->canvasBase(), syncObject->activeToolId));
if(tool && syncObject->smoothingOptions) {
tool->smoothingOptions()->setSmoothingType(syncObject->smoothingOptions->smoothingType());
tool->smoothingOptions()->setSmoothPressure(syncObject->smoothingOptions->smoothPressure());
tool->smoothingOptions()->setTailAggressiveness(syncObject->smoothingOptions->tailAggressiveness());
tool->smoothingOptions()->setUseScalableDistance(syncObject->smoothingOptions->useScalableDistance());
tool->smoothingOptions()->setSmoothnessDistance(syncObject->smoothingOptions->smoothnessDistance());
tool->smoothingOptions()->setUseDelayDistance(syncObject->smoothingOptions->useDelayDistance());
tool->smoothingOptions()->setDelayDistance(syncObject->smoothingOptions->delayDistance());
tool->smoothingOptions()->setFinishStabilizedCurve(syncObject->smoothingOptions->finishStabilizedCurve());
tool->smoothingOptions()->setStabilizeSensors(syncObject->smoothingOptions->stabilizeSensors());
tool->updateSettingsViews();
}
KisCanvasResourceProvider* provider = d->view->resourceProvider();
provider->setMirrorHorizontal(syncObject->mirrorHorizontal);
provider->setMirrorVertical(syncObject->mirrorVertical);
provider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, syncObject->mirrorAxesCenter);
provider->setPaintOpPreset(syncObject->paintOp);
qApp->processEvents();
KoToolManager::instance()->switchToolRequested("InteractionTool");
qApp->processEvents();
KoToolManager::instance()->switchToolRequested(syncObject->activeToolId);
qApp->processEvents();
provider->setBGColor(syncObject->backgroundColor);
provider->setFGColor(syncObject->foregroundColor);
provider->setHDRExposure(syncObject->exposure);
provider->setHDRGamma(syncObject->gamma);
provider->slotPatternActivated(syncObject->pattern);
provider->slotGradientActivated(syncObject->gradient);
provider->slotNodeActivated(syncObject->node);
provider->setOpacity(syncObject->opacity);
provider->setGlobalAlphaLock(syncObject->globalAlphaLock);
provider->setCurrentCompositeOp(syncObject->compositeOp);
d->view->document()->gridData().setGrid(syncObject->gridData->gridX(), syncObject->gridData->gridY());
d->view->document()->gridData().setGridColor(syncObject->gridData->gridColor());
d->view->document()->gridData().setPaintGridInBackground(syncObject->gridData->paintGridInBackground());
d->view->document()->gridData().setShowGrid(syncObject->gridData->showGrid());
d->view->document()->gridData().setSnapToGrid(syncObject->gridData->snapToGrid());
zoomIn();
qApp->processEvents();
d->view->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, syncObject->zoomLevel);
controller->rotateCanvas(syncObject->rotationAngle - d->view->canvasBase()->rotationAngle());
qApp->processEvents();
QPoint newOffset = syncObject->documentOffset;
controller->setScrollBarValue(newOffset);
}
return true;
}
case KisTabletEvent::TabletPressEx:
case KisTabletEvent::TabletReleaseEx:
emit interactionStarted();
d->canvas->globalInputManager()->eventFilter(this, event);
return true;
case KisTabletEvent::TabletMoveEx:
d->tabletEventCount++; //Note that this will wraparound at some point; This is intentional.
#ifdef Q_OS_X11
if(d->tabletEventCount % 2 == 0)
#endif
d->canvas->globalInputManager()->eventFilter(this, event);
return true;
case QEvent::KeyPress:
case QEvent::KeyRelease:
emit interactionStarted();
QApplication::sendEvent(d->view, event);
break;
default:
break;
}
return QDeclarativeItem::event( event );
}
bool KisSketchView::sceneEvent(QEvent* event)
{
if (d->canvas && d->canvasWidget) {
switch(event->type()) {
case QEvent::GraphicsSceneMousePress: {
QGraphicsSceneMouseEvent *gsmevent = static_cast<QGraphicsSceneMouseEvent*>(event);
QMouseEvent mevent(QMouseEvent::MouseButtonPress, gsmevent->pos().toPoint(), gsmevent->button(), gsmevent->buttons(), gsmevent->modifiers());
QApplication::sendEvent(d->canvasWidget, &mevent);
emit interactionStarted();
return true;
}
case QEvent::GraphicsSceneMouseMove: {
QGraphicsSceneMouseEvent *gsmevent = static_cast<QGraphicsSceneMouseEvent*>(event);
QMouseEvent mevent(QMouseEvent::MouseMove, gsmevent->pos().toPoint(), gsmevent->button(), gsmevent->buttons(), gsmevent->modifiers());
QApplication::sendEvent(d->canvasWidget, &mevent);
update();
emit interactionStarted();
return true;
}
case QEvent::GraphicsSceneMouseRelease: {
QGraphicsSceneMouseEvent *gsmevent = static_cast<QGraphicsSceneMouseEvent*>(event);
QMouseEvent mevent(QMouseEvent::MouseButtonRelease, gsmevent->pos().toPoint(), gsmevent->button(), gsmevent->buttons(), gsmevent->modifiers());
QApplication::sendEvent(d->canvasWidget, &mevent);
emit interactionStarted();
return true;
}
case QEvent::GraphicsSceneWheel: {
QGraphicsSceneWheelEvent *gswevent = static_cast<QGraphicsSceneWheelEvent*>(event);
QWheelEvent wevent(gswevent->pos().toPoint(), gswevent->delta(), gswevent->buttons(), gswevent->modifiers(), gswevent->orientation());
QApplication::sendEvent(d->canvasWidget, &wevent);
emit interactionStarted();
return true;
}
case QEvent::GraphicsSceneHoverEnter: {
QGraphicsSceneHoverEvent *hevent = static_cast<QGraphicsSceneHoverEvent*>(event);
QHoverEvent e(QEvent::Enter, hevent->screenPos(), hevent->lastScreenPos());
QApplication::sendEvent(d->canvasWidget, &e);
return true;
}
case QEvent::GraphicsSceneHoverLeave: {
QGraphicsSceneHoverEvent *hevent = static_cast<QGraphicsSceneHoverEvent*>(event);
QHoverEvent e(QEvent::Leave, hevent->screenPos(), hevent->lastScreenPos());
QApplication::sendEvent(d->canvasWidget, &e);
return true;
}
case QEvent::TouchBegin: {
QApplication::sendEvent(d->canvasWidget, event);
event->accept();
emit interactionStarted();
return true;
}
case QEvent::TabletPress:
case QEvent::TabletMove:
case QEvent::TabletRelease:
d->canvas->globalInputManager()->stopIgnoringEvents();
QApplication::sendEvent(d->canvasWidget, event);
return true;
default:
if (QApplication::sendEvent(d->canvasWidget, event)) {
emit interactionStarted();
return true;
}
}
}
return QDeclarativeItem::sceneEvent(event);
}
void KisSketchView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
{
if (d->canvasWidget && !newGeometry.isEmpty()) {
d->view->resize(newGeometry.toRect().size());
// If we don't ask for this event to be sent, the view does not actually handle
// the resize, and we're stuck with a very oddly sized viewport
QResizeEvent *event = new QResizeEvent(newGeometry.toRect().size(), d->view->size());
QApplication::sendEvent(d->view, event);
// This is a touch on the hackish side - i'm sure there's a better way of doing it
// but it's taking a long time to work it out. Problem: When switching orientation,
// the canvas is rendered wrong, in what looks like an off-by-one ish kind of fashion.
if (oldGeometry.height() == oldGeometry.width() && oldGeometry.height() == newGeometry.width()) {
// in this case, we've just rotated the display... do something useful!
// Turns out we get /two/ resize events per rotation, one one per setting each height and width.
// So we can't just check it normally. Annoying, but there you go.
QTimer::singleShot(100, this, SLOT(centerDoc()));
QTimer::singleShot(150, this, SLOT(zoomOut()));
}
if (oldGeometry.height() == oldGeometry.width() && oldGeometry.width() == newGeometry.height()) {
// in this case, we've just rotated the display... do something useful!
// Turns out we get /two/ resize events per rotation, one one per setting each height and width.
// So we can't just check it normally. Annoying, but there you go.
QTimer::singleShot(100, this, SLOT(centerDoc()));
QTimer::singleShot(150, this, SLOT(zoomOut()));
}
}
}
void KisSketchView::centerDoc()
{
d->viewManager->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0);
}
void KisSketchView::Private::imageUpdated(const QRect &updated)
{
if (q->scene()) {
q->scene()->views().at(0)->update(updated);
q->scene()->invalidate( 0, 0, q->width(), q->height() );
}
}
void KisSketchView::Private::documentOffsetMoved()
{
if (q->scene()) {
q->scene()->views().at(0)->update();
q->scene()->invalidate( 0, 0, q->width(), q->height() );
}
}
void KisSketchView::Private::resetDocumentPosition()
{
viewManager->zoomController()->setZoomMode(KoZoomMode::ZOOM_PAGE);
QPoint pos;
KisCanvasController *controller = dynamic_cast<KisCanvasController*>(viewManager->canvasBase()->canvasController());
if (!controller) return;
QScrollBar *sb = controller->horizontalScrollBar();
pos.rx() = sb->minimum() + (sb->maximum() - sb->minimum()) / 2;
sb = controller->verticalScrollBar();
pos.ry() = sb->minimum() + (sb->maximum() - sb->minimum()) / 2;
controller->setScrollBarValue(pos);
}
void KisSketchView::Private::removeNodeAsync(KisNodeSP removedNode)
{
if (removedNode) {
imageUpdated(removedNode->extent());
}
}
void KisSketchView::Private::zoomChanged()
{
if (q->scene()) {
q->scene()->views().at(0)->update();
q->scene()->invalidate( 0, 0, q->width(), q->height() );
}
}
void KisSketchView::activate()
{
if (d->canvasWidget != d->canvas->canvasWidget()) {
d->canvasWidget = d->canvas->canvasWidget();
SketchDeclarativeView *v = qobject_cast<SketchDeclarativeView*>(scene()->views().at(0));
if (v) {
v->setCanvasWidget(d->canvasWidget);
v->setDrawCanvas(true);
}
}
d->canvasWidget->setFocus();
Q_ASSERT(d->viewManager);
KisCanvasController *controller = dynamic_cast<KisCanvasController*>(d->viewManager->canvasBase()->canvasController());
Q_ASSERT(controller);
controller->activate();
}
diff --git a/krita/sketch/MainWindow.cpp b/krita/sketch/MainWindow.cpp
index 480a1049648..109d70a402c 100644
--- a/krita/sketch/MainWindow.cpp
+++ b/krita/sketch/MainWindow.cpp
@@ -1,247 +1,247 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
* Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt <boud@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "MainWindow.h"
#include "opengl/kis_opengl.h"
#include <QApplication>
#include <QResizeEvent>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QDir>
#include <QFile>
#include <QMessageBox>
#include <QFileInfo>
#include <QGLWidget>
#include <QTimer>
#include <kurl.h>
#include <kstandarddirs.h>
#include <kdialog.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include "filter/kis_filter.h"
#include "filter/kis_filter_registry.h"
#include "kis_paintop.h"
#include "kis_paintop_registry.h"
#include <KoZoomController.h>
#include <KoIcon.h>
#include "KisViewManager.h"
#include <kis_canvas_controller.h>
#include "kis_config.h"
#include <KisDocument.h>
#include "SketchDeclarativeView.h"
#include "RecentFileManager.h"
#include "DocumentManager.h"
#include "QmlGlobalEngine.h"
#include "Settings.h"
class MainWindow::Private
{
public:
Private(MainWindow* qq)
: q(qq)
, allowClose(true)
, viewManager(0)
{
centerer = new QTimer(q);
centerer->setInterval(10);
centerer->setSingleShot(true);
connect(centerer, SIGNAL(timeout()), q, SLOT(adjustZoomOnDocumentChangedAndStuff()));
}
MainWindow* q;
bool allowClose;
KisViewManager* viewManager;
QString currentSketchPage;
QTimer *centerer;
};
MainWindow::MainWindow(QStringList fileNames, QWidget* parent, Qt::WindowFlags flags)
: QMainWindow(parent, flags ), d( new Private(this))
{
qApp->setActiveWindow(this);
setWindowTitle(i18n("Krita Sketch"));
setWindowIcon(koIcon("kritasketch"));
// Load filters and other plugins in the gui thread
Q_UNUSED(KisFilterRegistry::instance());
Q_UNUSED(KisPaintOpRegistry::instance());
KisConfig cfg;
cfg.setNewCursorStyle(CURSOR_STYLE_NO_CURSOR);
cfg.setNewOutlineStyle(OUTLINE_NONE);
cfg.setUseOpenGL(true);
foreach(QString fileName, fileNames) {
DocumentManager::instance()->recentFileManager()->addRecent(fileName);
}
connect(DocumentManager::instance(), SIGNAL(documentChanged()), SLOT(resetWindowTitle()));
connect(DocumentManager::instance(), SIGNAL(documentSaved()), SLOT(resetWindowTitle()));
QDeclarativeView* view = new SketchDeclarativeView();
QmlGlobalEngine::instance()->setEngine(view->engine());
view->engine()->rootContext()->setContextProperty("mainWindow", this);
#ifdef Q_OS_WIN
QDir appdir(qApp->applicationDirPath());
// Corrects for mismatched case errors in path (qtdeclarative fails to load)
wchar_t buffer[1024];
QString absolute = appdir.absolutePath();
DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
rv = ::GetLongPathName(buffer, buffer, 1024);
QString correctedPath((QChar *)buffer);
appdir.setPath(correctedPath);
// for now, the app in bin/ and we still use the env.bat script
appdir.cdUp();
view->engine()->addImportPath(appdir.canonicalPath() + "/lib/calligra/imports");
view->engine()->addImportPath(appdir.canonicalPath() + "/lib64/calligra/imports");
QString mainqml = appdir.canonicalPath() + "/share/apps/kritasketch/kritasketch.qml";
#else
view->engine()->addImportPath(KGlobal::dirs()->findDirs("lib", "calligra/imports").value(0));
QString mainqml = KGlobal::dirs()->findResource("data", "kritasketch/kritasketch.qml");
#endif
Q_ASSERT(QFile::exists(mainqml));
if (!QFile::exists(mainqml)) {
QMessageBox::warning(0, i18nc("@title:window", "No QML found"), mainqml + " doesn't exist.");
}
QFileInfo fi(mainqml);
view->setSource(QUrl::fromLocalFile(fi.canonicalFilePath()));
view->setResizeMode( QDeclarativeView::SizeRootObjectToView );
if (view->errors().count() > 0) {
foreach(const QDeclarativeError &error, view->errors()) {
- kDebug() << error.toString();
+ dbgKrita << error.toString();
}
}
setCentralWidget(view);
}
void MainWindow::resetWindowTitle()
{
KUrl url(DocumentManager::instance()->settingsManager()->currentFile());
QString fileName = url.fileName();
if(url.protocol() == "temp")
fileName = i18n("Untitled");
KDialog::CaptionFlags flags = KDialog::HIGCompliantCaption;
KisDocument* document = DocumentManager::instance()->document();
if (document && document->isModified() ) {
flags |= KDialog::ModifiedCaption;
}
setWindowTitle( KDialog::makeStandardCaption(fileName, this, flags) );
}
bool MainWindow::allowClose() const
{
return d->allowClose;
}
void MainWindow::setAllowClose(bool allow)
{
d->allowClose = allow;
}
QString MainWindow::currentSketchPage() const
{
return d->currentSketchPage;
}
void MainWindow::setCurrentSketchPage(QString newPage)
{
d->currentSketchPage = newPage;
emit currentSketchPageChanged();
}
void MainWindow::adjustZoomOnDocumentChangedAndStuff()
{
if (d->viewManager) {
qApp->processEvents();
d->viewManager->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0);
qApp->processEvents();
QPoint center = d->viewManager->canvas()->rect().center();
static_cast<KoCanvasControllerWidget*>(d->viewManager->canvasBase()->canvasController())->zoomRelativeToPoint(center, 0.9);
qApp->processEvents();
}
}
QObject* MainWindow::sketchKisView() const
{
return d->viewManager;
}
void MainWindow::setSketchKisView(QObject* newView)
{
if (d->viewManager)
d->viewManager->disconnect(this);
if (d->viewManager != newView)
{
d->viewManager = qobject_cast<KisViewManager*>(newView);
connect(d->viewManager, SIGNAL(sigLoadingFinished()), d->centerer, SLOT(start()));
d->centerer->start();
emit sketchKisViewChanged();
}
}
void MainWindow::minimize()
{
setWindowState(windowState() ^ Qt::WindowMinimized);
}
void MainWindow::closeWindow()
{
//For some reason, close() does not work even if setAllowClose(true) was called just before this method.
//So instead just completely quit the application, since we are using a single window anyway.
QApplication::exit();
}
void MainWindow::resizeEvent(QResizeEvent* event)
{
// TODO this needs setting somewhere...
// d->constants->setGridWidth( event->size().width() / d->constants->gridColumns() );
// d->constants->setGridHeight( event->size().height() / d->constants->gridRows() );
QWidget::resizeEvent(event);
}
void MainWindow::closeEvent(QCloseEvent* event)
{
if (!d->allowClose) {
event->ignore();
emit closeRequested();
} else {
event->accept();
}
}
MainWindow::~MainWindow()
{
delete d;
}
diff --git a/krita/sketch/SketchApplication.cpp b/krita/sketch/SketchApplication.cpp
index a6759c57983..76108ba559b 100644
--- a/krita/sketch/SketchApplication.cpp
+++ b/krita/sketch/SketchApplication.cpp
@@ -1,96 +1,96 @@
/*
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "SketchApplication.h"
#include <KoPluginLoader.h>
#include <KoShapeRegistry.h>
#include <KoDpi.h>
#include "KoGlobal.h"
#include <kcrash.h>
#include <klocale.h>
#include <kcmdlineargs.h>
#include <kdesktopfile.h>
#include <QMessageBox>
#include <kstandarddirs.h>
#include <kiconloader.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kmimetype.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <krecentdirs.h>
#include <QFile>
#include <QWidget>
#include <QSysInfo>
#include <QStringList>
#include <QDesktopServices>
#include <QProcessEnvironment>
#include <QDir>
#include <QDesktopWidget>
#include <stdlib.h>
#include "KisPrintJob.h"
#include "KisDocumentEntry.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "kis_factory2.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
#include "flake/kis_shape_selection.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator_registry.h>
#include <generator/kis_generator.h>
#include <kis_paintop_registry.h>
#include <metadata/kis_meta_data_io_backend.h>
#include "kisexiv2/kis_exiv2.h"
SketchApplication::SketchApplication()
: KApplication()
{
}
void SketchApplication::start()
{
// Load various global plugins
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
// Load the krita-specific tools
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// Load dockers
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// XXX_EXIV: make the exiv io backends real plugins
KisExiv2::initialize();
}
diff --git a/krita/sketch/Theme.cpp b/krita/sketch/Theme.cpp
index 806f553e325..5dffbd8a126 100644
--- a/krita/sketch/Theme.cpp
+++ b/krita/sketch/Theme.cpp
@@ -1,418 +1,418 @@
/*
* This file is part of the KDE project
* Copyright (C) 2014 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Theme.h"
#include <QStringList>
#include <QUrl>
-#include <QDebug>
+#include <kis_debug.h>
#include <QFile>
#include <QDir>
#include <QColor>
#include <QFont>
#include <QFontDatabase>
#include <QApplication>
#include <QWidget>
#include <QDeclarativeComponent>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kurl.h>
#include "QmlGlobalEngine.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
class Theme::Private
{
public:
Private()
: inheritedTheme(0)
, iconPath("icons/")
, imagePath("images/")
, fontPath("fonts/")
, fontsAdded(false)
, lineCountLandscape(40)
, lineCountPortrait(70)
{ }
void rebuildFontCache();
QString id;
QString name;
QString inherits;
Theme* inheritedTheme;
QVariantMap colors;
QVariantMap sizes;
QVariantMap fonts;
QString basePath;
QString iconPath;
QString imagePath;
QString fontPath;
QHash<QString, QColor> colorCache;
QHash<QString, QFont> fontMap;
bool fontsAdded;
QList<int> addedFonts;
int lineCountLandscape;
int lineCountPortrait;
};
Theme::Theme(QObject* parent)
: QObject(parent), d(new Private)
{
qApp->installEventFilter(this);
}
Theme::~Theme()
{
QFontDatabase db;
Q_FOREACH(int id, d->addedFonts) {
db.removeApplicationFont(id);
}
delete d;
}
QString Theme::id() const
{
return d->id;
}
void Theme::setId(const QString& newValue)
{
if(newValue != d->id) {
d->id = newValue;
d->basePath = KUrl(KGlobal::dirs()->findResource("data", QString("kritasketch/themes/%1/theme.qml").arg(d->id))).directory();
emit idChanged();
}
}
QString Theme::name() const
{
return d->name;
}
void Theme::setName(const QString& newValue)
{
if(newValue != d->name) {
d->name = newValue;
emit nameChanged();
}
}
QString Theme::inherits() const
{
return d->inherits;
}
void Theme::setInherits(const QString& newValue)
{
if(newValue != d->inherits) {
if(d->inheritedTheme) {
delete d->inheritedTheme;
d->inheritedTheme = 0;
}
d->inherits = newValue;
if(!d->inherits.isEmpty()) {
d->inheritedTheme = Theme::load(d->inherits, this);
connect(d->inheritedTheme, SIGNAL(fontCacheRebuilt()), SIGNAL(fontCacheRebuilt()));
}
emit inheritsChanged();
}
}
QVariantMap Theme::colors() const
{
return d->colors;
}
void Theme::setColors(const QVariantMap& newValue)
{
if(newValue != d->colors) {
d->colors = newValue;
emit colorsChanged();
}
}
QColor Theme::color(const QString& name)
{
if(d->colorCache.contains(name))
return d->colorCache.value(name);
QStringList parts = name.split('/');
QColor result;
if(!parts.isEmpty())
{
QVariantMap map = d->colors;
QString current = parts.takeFirst();
while(map.contains(current))
{
QVariant value = map.value(current);
if(value.type() == QVariant::Map)
{
if(parts.isEmpty())
break;
map = value.toMap();
current = parts.takeFirst();
}
else
{
result = value.value<QColor>();
map = QVariantMap();
}
}
}
if(!result.isValid() && d->inheritedTheme) {
result = d->inheritedTheme->color(name);
}
if(!result.isValid()) {
- qWarning() << "Unable to find color" << name;
+ warnKrita << "Unable to find color" << name;
} else {
d->colorCache.insert(name, result);
}
return result;
}
QVariantMap Theme::sizes() const
{
return d->sizes;
}
void Theme::setSizes(const QVariantMap& newValue)
{
if(newValue != d->sizes) {
d->sizes = newValue;
emit sizesChanged();
}
}
float Theme::size(const QString& name)
{
Q_UNUSED(name);
return 0.f;
}
QVariantMap Theme::fonts() const
{
return d->fonts;
}
void Theme::setFonts(const QVariantMap& newValue)
{
if(newValue != d->fonts)
{
d->fonts = newValue;
d->fontMap.clear();
emit fontsChanged();
}
}
QFont Theme::font(const QString& name)
{
if(!d->fontsAdded) {
QDir fontDir(d->basePath + '/' + d->fontPath);
QStringList entries = fontDir.entryList(QDir::Files);
QFontDatabase db;
Q_FOREACH(QString entry, entries) {
d->addedFonts.append(db.addApplicationFont(fontDir.absoluteFilePath(entry)));
}
d->fontsAdded = true;
}
if(d->fontMap.isEmpty()) {
d->rebuildFontCache();
}
if(d->fontMap.contains(name))
return d->fontMap.value(name);
if(d->inheritedTheme)
return d->inheritedTheme->font(name);
- qWarning() << "Unable to find font" << name;
+ warnKrita << "Unable to find font" << name;
return QFont();
}
QString Theme::fontPath() const
{
return d->fontPath;
}
void Theme::setFontPath(const QString& newValue)
{
if(newValue != d->fontPath) {
if(!d->addedFonts.isEmpty()) {
QFontDatabase db;
Q_FOREACH(int id, d->addedFonts) {
db.removeApplicationFont(id);
}
d->addedFonts.clear();
}
d->fontPath = newValue;
d->fontsAdded = false;
emit fontPathChanged();
}
}
QString Theme::iconPath() const
{
return d->iconPath;
}
void Theme::setIconPath(const QString& newValue)
{
if(newValue != d->iconPath) {
d->iconPath = newValue;
emit iconPathChanged();
}
}
QUrl Theme::icon(const QString& name)
{
QString url = QString("%1/%2/%3.svg").arg(d->basePath, d->iconPath, name);
if(!QFile::exists(url)) {
if(d->inheritedTheme) {
return d->inheritedTheme->icon(name);
} else {
- qWarning() << "Unable to find icon" << url;
+ warnKrita << "Unable to find icon" << url;
}
}
return QUrl::fromLocalFile(url);
}
QString Theme::imagePath() const
{
return d->imagePath;
}
void Theme::setImagePath(const QString& newValue)
{
if(newValue != d->imagePath) {
d->imagePath = newValue;
emit imagePathChanged();
}
}
QUrl Theme::image(const QString& name)
{
QString url = QString("%1/%2/%3").arg(d->basePath, d->imagePath, name);
if(!QFile::exists(url)) {
if(d->inheritedTheme) {
return d->inheritedTheme->image(name);
} else {
- qWarning() << "Unable to find image" << url;
+ warnKrita << "Unable to find image" << url;
}
}
return QUrl::fromLocalFile(url);
}
Theme* Theme::load(const QString& id, QObject* parent)
{
QString qml;
//Ugly hacky stuff for making things work on Windows
#ifdef Q_OS_WIN
QDir appdir(qApp->applicationDirPath());
// Corrects for mismatched case errors in path (qtdeclarative fails to load)
wchar_t buffer[1024];
QString absolute = appdir.absolutePath();
DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024);
rv = ::GetLongPathName(buffer, buffer, 1024);
QString correctedPath((QChar *)buffer);
appdir.setPath(correctedPath);
// for now, the app in bin/ and we still use the env.bat script
appdir.cdUp();
qml = QString("%1/share/apps/kritasketch/themes/%2/theme.qml").arg(appdir.canonicalPath(), id);
#else
qml = KGlobal::dirs()->findResource("data", QString("kritasketch/themes/%1/theme.qml").arg(id));
#endif
QDeclarativeComponent themeComponent(QmlGlobalEngine::instance()->engine(), parent);
themeComponent.loadUrl(QUrl::fromLocalFile(qml));
if(themeComponent.isError()) {
- qWarning() << themeComponent.errorString();
+ warnKrita << themeComponent.errorString();
return 0;
}
Theme* theme = qobject_cast<Theme*>(themeComponent.create());
if(!theme) {
- qWarning() << "Failed to create theme instance!";
+ warnKrita << "Failed to create theme instance!";
return 0;
}
return theme;
}
bool Theme::eventFilter(QObject* target, QEvent* event)
{
if(target == qApp->activeWindow() && target->inherits("QMainWindow") && event->type() == QEvent::Resize) {
d->rebuildFontCache();
emit fontCacheRebuilt();
}
return QObject::eventFilter(target, event);
}
void Theme::Private::rebuildFontCache()
{
fontMap.clear();
QFontDatabase db;
for(QVariantMap::iterator itr = fonts.begin(); itr != fonts.end(); ++itr)
{
QVariantMap map = itr->toMap();
if(map.isEmpty())
continue;
QFont font = db.font(map.value("family").toString(), map.value("style", "Regular").toString(), 10);
if(font.isCopyOf(qApp->font()))
- qWarning() << "Could not find font" << map.value("family") << "with style" << map.value("style", "Regular");
+ warnKrita << "Could not find font" << map.value("family") << "with style" << map.value("style", "Regular");
float lineCount = qApp->activeWindow()->height() > qApp->activeWindow()->width() ? lineCountPortrait : lineCountLandscape;
float lineHeight = qApp->activeWindow()->height() / lineCount;
font.setPixelSize(lineHeight * map.value("size", 1).toFloat());
fontMap.insert(itr.key(), font);
}
}
diff --git a/krita/sketch/models/LayerModel.cpp b/krita/sketch/models/LayerModel.cpp
index 9783790c7df..83e8e76b570 100644
--- a/krita/sketch/models/LayerModel.cpp
+++ b/krita/sketch/models/LayerModel.cpp
@@ -1,1055 +1,1055 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Dan Leinir Turthra Jensen <admin@leinir.dk>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "LayerModel.h"
#include "LayerThumbProvider.h"
#include <PropertyContainer.h>
#include <kis_node_model.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <kis_node_manager.h>
#include <kis_dummies_facade_base.h>
#include <KisDocument.h>
#include <kis_composite_ops_model.h>
#include <kis_node.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_filter_mask.h>
#include <kis_shape_controller.h>
#include <kis_adjustment_layer.h>
#include <kis_selection_manager.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_configuration.h>
#include <filter/kis_filter_registry.h>
#include <KoShapeBasedDocumentBase.h>
#include <KoProperties.h>
#include <QDeclarativeEngine>
struct LayerModelMetaInfo {
LayerModelMetaInfo()
: canMoveUp(false)
, canMoveRight(false)
, canMoveDown(false)
, canMoveLeft(false)
, depth(-1)
{}
bool canMoveUp;
bool canMoveRight;
bool canMoveDown;
bool canMoveLeft;
int depth;
};
class LayerModel::Private {
public:
Private(LayerModel* qq)
: q(qq)
, nodeModel(new KisNodeModel(qq))
, aboutToRemoveRoots(false)
, canvas(0)
, nodeManager(0)
, image(0)
, activeNode(0)
, declarativeEngine(0)
, thumbProvider(0)
, updateActiveLayerWithNewFilterConfigTimer(new QTimer(qq))
, imageChangedTimer(new QTimer(qq))
{
QList<KisFilterSP> tmpFilters = KisFilterRegistry::instance()->values();
foreach(const KisFilterSP& filter, tmpFilters)
{
filters[filter.data()->id()] = filter.data();
}
updateActiveLayerWithNewFilterConfigTimer->setInterval(0);
updateActiveLayerWithNewFilterConfigTimer->setSingleShot(true);
connect(updateActiveLayerWithNewFilterConfigTimer, SIGNAL(timeout()), qq, SLOT(updateActiveLayerWithNewFilterConfig()));
imageChangedTimer->setInterval(250);
imageChangedTimer->setSingleShot(true);
connect(imageChangedTimer, SIGNAL(timeout()), qq, SLOT(imageHasChanged()));
}
LayerModel* q;
QList<KisNodeSP> layers;
QHash<const KisNode*, LayerModelMetaInfo> layerMeta;
KisNodeModel* nodeModel;
bool aboutToRemoveRoots;
KisViewManager* view;
KisCanvas2* canvas;
QPointer<KisNodeManager> nodeManager;
KisImageWSP image;
KisNodeSP activeNode;
QDeclarativeEngine* declarativeEngine;
LayerThumbProvider* thumbProvider;
QHash<QString, const KisFilter*> filters;
KisFilterConfiguration* newConfig;
QTimer* updateActiveLayerWithNewFilterConfigTimer;
QTimer* imageChangedTimer;
static int counter()
{
static int count = 0;
return count++;
}
static QStringList layerClassNames()
{
QStringList list;
list << "KisGroupLayer";
list << "KisPaintLayer";
list << "KisFilterMask";
list << "KisAdjustmentLayer";
return list;
}
int deepChildCount(KisNodeSP layer)
{
quint32 childCount = layer->childCount();
QList<KisNodeSP> children = layer->childNodes(layerClassNames(), KoProperties());
for(quint32 i = 0; i < childCount; ++i)
childCount += deepChildCount(children.at(i));
return childCount;
}
void rebuildLayerList(KisNodeSP layer = 0)
{
bool refreshingFromRoot = false;
if (!image)
{
layers.clear();
return;
}
if (layer == 0)
{
refreshingFromRoot = true;
layers.clear();
layer = image->rootLayer();
}
// implementation node: The root node is not a visible node, and so
// is never added to the list of layers
QList<KisNodeSP> children = layer->childNodes(layerClassNames(), KoProperties());
if (children.count() == 0)
return;
for(quint32 i = children.count(); i > 0; --i)
{
layers << children.at(i-1);
rebuildLayerList(children.at(i-1));
}
if (refreshingFromRoot)
refreshLayerMovementAbilities();
}
void refreshLayerMovementAbilities()
{
layerMeta.clear();
if (layers.count() == 0)
return;
for(int i = 0; i < layers.count(); ++i)
{
const KisNodeSP layer = layers.at(i);
LayerModelMetaInfo ability;
if (i > 0)
ability.canMoveUp = true;
if (i < layers.count() - 1)
ability.canMoveDown = true;
KisNodeSP parent = layer;
while(parent)
{
++ability.depth;
parent = parent->parent();
}
if (ability.depth > 1)
ability.canMoveLeft = true;
if (i < layers.count() - 1 && qobject_cast<const KisGroupLayer*>(layers.at(i + 1).constData()))
ability.canMoveRight = true;
layerMeta[layer] = ability;
}
}
};
LayerModel::LayerModel(QObject* parent)
: QAbstractListModel(parent)
, d(new Private(this))
{
QHash<int, QByteArray> roles;
roles[IconRole] = "icon";
roles[NameRole] = "name";
roles[ActiveLayerRole] = "activeLayer";
roles[OpacityRole] = "opacity";
roles[PercentOpacityRole] = "percentOpacity";
roles[VisibleRole] = "visible";
roles[LockedRole] = "locked";
roles[CompositeDetailsRole] = "compositeDetails";
roles[FilterRole] = "filter";
roles[ChildCountRole] = "childCount";
roles[DeepChildCountRole] = "deepChildCount";
roles[DepthRole] = "depth";
roles[PreviousItemDepthRole] = "previousItemDepth";
roles[NextItemDepthRole] = "nextItemDepth";
roles[CanMoveDownRole] = "canMoveDown";
roles[CanMoveLeftRole] = "canMoveLeft";
roles[CanMoveRightRole] = "canMoveRight";
roles[CanMoveUpRole] = "canMoveUp";
setRoleNames(roles);
connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(source_rowsAboutToBeInserted(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
this, SLOT(source_rowsInserted(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(source_rowsAboutToBeRemoved(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
this, SLOT(source_rowsRemoved(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(source_dataChanged(QModelIndex,QModelIndex)));
connect(d->nodeModel, SIGNAL(modelReset()),
this, SLOT(source_modelReset()));
connect(d->nodeModel, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged()));
connect(d->nodeModel, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()));
}
LayerModel::~LayerModel()
{
delete d;
}
QObject* LayerModel::view() const
{
return d->view;
}
void LayerModel::setView(QObject *newView)
{
KisViewManager* view = qobject_cast<KisViewManager*>(newView);
// This has to happen very early, and we will be filling it back up again soon anyway...
if (d->canvas) {
d->canvas->disconnectCanvasObserver(this);
disconnect(d->image, 0, this, 0);
disconnect(d->nodeManager, 0, this, 0);
disconnect(d->nodeModel, 0, d->nodeManager, 0);
disconnect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
d->image = 0;
d->nodeManager = 0;
d->layers.clear();
d->activeNode.clear();
d->canvas = 0;
d->nodeModel->setDummiesFacade(0, 0, 0);
}
d->view = view;
if (!d->view) {
return;
}
d->canvas = view->canvasBase();
d->thumbProvider = new LayerThumbProvider();
d->thumbProvider->setLayerModel(this);
d->thumbProvider->setLayerID(Private::counter());
d->declarativeEngine->addImageProvider(QString("layerthumb%1").arg(d->thumbProvider->layerID()), d->thumbProvider);
if (d->canvas) {
d->image = d->canvas->imageView()->image();
d->nodeManager = d->canvas->viewManager()->nodeManager();
KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(d->canvas->imageView()->document()->shapeController());
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(d->canvas->imageView()->document()->shapeController());
d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController);
connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP)));
connect(d->image, SIGNAL(sigImageUpdated(QRect)), SLOT(imageChanged()));
connect(d->image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(aboutToRemoveNode(KisNodeSP)));
// cold start
currentNodeChanged(d->nodeManager->activeNode());
// Connection KisNodeManager -> KisLayerBox
connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
// Connection KisLayerBox -> KisNodeManager
// The order of these connections is important! See comment in the ctor
connect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), d->nodeManager, SLOT(slotUiActivatedNode(KisNodeSP)));
connect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), SLOT(currentNodeChanged(KisNodeSP)));
// Node manipulation methods are forwarded to the node manager
connect(d->nodeModel, SIGNAL(requestAddNode(KisNodeSP, KisNodeSP, KisNodeSP)),
d->nodeManager, SLOT(addNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP)));
connect(d->nodeModel, SIGNAL(requestMoveNode(KisNodeSP, KisNodeSP, KisNodeSP)),
d->nodeManager, SLOT(moveNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP)));
d->rebuildLayerList();
reset();
}
}
QObject* LayerModel::engine() const
{
return d->declarativeEngine;
}
void LayerModel::setEngine(QObject* newEngine)
{
d->declarativeEngine = qobject_cast<QDeclarativeEngine*>(newEngine);
emit engineChanged();
}
QString LayerModel::fullImageThumbUrl() const
{
return QString("image://layerthumb%1/fullimage/%2").arg(d->thumbProvider->layerID()).arg(QDateTime::currentMSecsSinceEpoch());
}
void LayerModel::currentNodeChanged(KisNodeSP newActiveNode)
{
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
d->activeNode = newActiveNode;
emitActiveChanges();
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
}
QVariant LayerModel::data(const QModelIndex& index, int role) const
{
QVariant data;
if (index.isValid()) {
index.internalPointer();
KisNodeSP node = d->layers.at(index.row());
if (node.isNull())
return data;
KisNodeSP parent;
switch(role)
{
case IconRole:
if (dynamic_cast<const KisGroupLayer*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_group-black.svg");
else if (dynamic_cast<const KisFilterMask*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else if (dynamic_cast<const KisAdjustmentLayer*>(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else
// We add the currentMSecsSinceEpoch to ensure we force an update (even with cache turned
// off, we still apparently get some caching behaviour on delegates in QML)
data = QString("image://layerthumb%1/%2/%3").arg(d->thumbProvider->layerID()).arg(index.row()).arg(QDateTime::currentMSecsSinceEpoch());
break;
case NameRole:
data = node->name();
break;
case ActiveLayerRole:
data = (node == d->activeNode);
break;
case OpacityRole:
data = node->opacity();
break;
case PercentOpacityRole:
data = node->percentOpacity();
break;
case VisibleRole:
data = node->visible();
break;
case LockedRole:
data = node->userLocked();
break;
case CompositeDetailsRole:
// composite op goes here...
if (node->compositeOp())
data = node->compositeOp()->description();
break;
case FilterRole:
break;
case ChildCountRole:
data = node->childNodes(d->layerClassNames(), KoProperties()).count();
break;
case DeepChildCountRole:
data = d->deepChildCount(d->layers.at(index.row()));
break;
case DepthRole:
data = d->layerMeta[node.data()].depth;
break;
case PreviousItemDepthRole:
if (index.row() == 0)
data = -1;
else
data = d->layerMeta[d->layers[index.row() - 1].data()].depth;
break;
case NextItemDepthRole:
if (index.row() == d->layers.count() - 1)
data = -1;
else
data = d->layerMeta[d->layers[index.row() + 1].data()].depth;
break;
case CanMoveDownRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveDown;
break;
case CanMoveLeftRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveLeft;
break;
case CanMoveRightRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveRight;
break;
case CanMoveUpRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveUp;
break;
default:
break;
}
}
return data;
}
int LayerModel::rowCount(const QModelIndex& parent) const
{
if ( parent.isValid() ) {
return 0;
}
return d->layers.count();
}
QVariant LayerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return QAbstractItemModel::headerData(section, orientation, role);
}
void LayerModel::setActive(int index)
{
if (index > -1 && index < d->layers.count()) {
KisNodeSP newNode = d->layers.at(index);
d->nodeManager->slotUiActivatedNode(newNode);
currentNodeChanged(newNode);
}
}
void LayerModel::moveUp()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->nextSibling()) {
- //qDebug() << "Active node apparently has no next sibling, however that has happened...";
+ //dbgKrita << "Active node apparently has no next sibling, however that has happened...";
if (!grandParent)
return;
- //qDebug() << "Node has grandparent";
+ //dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
- //qDebug() << "Node isn't a mask";
+ //dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
else {
- //qDebug() << "Move node directly";
+ //dbgKrita << "Move node directly";
d->nodeManager->lowerNode();
}
}
void LayerModel::moveDown()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->prevSibling()) {
- //qDebug() << "Active node apparently has no previous sibling, however that has happened...";
+ //dbgKrita << "Active node apparently has no previous sibling, however that has happened...";
if (!grandParent)
return;
- //qDebug() << "Node has grandparent";
+ //dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
- //qDebug() << "Node isn't a mask";
+ //dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
} else
{
- //qDebug() << "Move node directly";
+ //dbgKrita << "Move node directly";
d->nodeManager->raiseNode();
}
}
void LayerModel::moveLeft()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
quint16 nodeIndex = parent->index(node);
if (!grandParent)
return;
if (!grandParent->parent() && node->inherits("KisMask"))
return;
if (nodeIndex <= parent->childCount() / 2) {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
}
else {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
}
void LayerModel::moveRight()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = d->nodeManager->activeNode()->parent();
KisNodeSP newParent;
int nodeIndex = parent->index(node);
int indexAbove = nodeIndex + 1;
int indexBelow = nodeIndex - 1;
if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) {
newParent = parent->at(indexBelow);
d->nodeManager->moveNodeAt(node, newParent, newParent->childCount());
}
else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) {
newParent = parent->at(indexAbove);
d->nodeManager->moveNodeAt(node, newParent, 0);
}
else {
return;
}
}
void LayerModel::clear()
{
d->canvas->viewManager()->selectionManager()->clear();
}
void LayerModel::clone()
{
d->nodeManager->duplicateActiveNode();
}
void LayerModel::setLocked(int index, bool newLocked)
{
if (index > -1 && index < d->layers.count()) {
if(d->layers[index]->userLocked() == newLocked)
return;
d->layers[index]->setUserLocked(newLocked);
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setOpacity(int index, float newOpacity)
{
if (index > -1 && index < d->layers.count()) {
if(qFuzzyCompare(d->layers[index]->opacity() + 1, newOpacity + 1))
return;
d->layers[index]->setOpacity(newOpacity);
d->layers[index]->setDirty();
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setVisible(int index, bool newVisible)
{
if (index > -1 && index < d->layers.count()) {
KisDocumentSectionModel::PropertyList props = d->layers[index]->sectionModelProperties();
KisDocumentSectionModel::Property prop = props[0];
if(props[0].state == newVisible)
return;
props[0] = KisDocumentSectionModel::Property(prop.name, prop.onIcon, prop.offIcon, newVisible);
d->nodeModel->setData( d->nodeModel->indexFromNode(d->layers[index]), QVariant::fromValue<KisDocumentSectionModel::PropertyList>(props), KisDocumentSectionModel::PropertiesRole );
d->layers[index]->setDirty(d->layers[index]->extent());
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
QImage LayerModel::layerThumbnail(QString layerID) const
{
// So, yeah, this is a complete cheatery hack. However, it ensures
// we actually get updates when we want them (every time the image is supposed
// to be changed). Had hoped we could avoid it, but apparently not.
int index = layerID.section(QChar('/'), 0, 0).toInt();
QImage thumb;
if (index > -1 && index < d->layers.count()) {
if (d->thumbProvider)
thumb = d->layers[index]->createThumbnail(120, 120);
}
return thumb;
}
void LayerModel::deleteCurrentLayer()
{
d->activeNode.clear();
d->nodeManager->removeNode();
}
void LayerModel::deleteLayer(int index)
{
if (index > -1 && index < d->layers.count()) {
if (d->activeNode == d->layers.at(index))
d->activeNode.clear();
d->nodeManager->slotUiActivatedNode(d->layers.at(index));
d->nodeManager->removeNode();
d->rebuildLayerList();
reset();
}
}
void LayerModel::addLayer(int layerType)
{
switch(layerType) {
case 0:
d->nodeManager->createNode("KisPaintLayer");
break;
case 1:
d->nodeManager->createNode("KisGroupLayer");
break;
case 2:
d->nodeManager->createNode("KisFilterMask", true);
break;
default:
break;
}
}
void LayerModel::source_rowsAboutToBeInserted(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsInserted(QModelIndex /*p*/, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_rowsAboutToBeRemoved(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsRemoved(QModelIndex, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_dataChanged(QModelIndex /*tl*/, QModelIndex /*br*/)
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::source_modelReset()
{
beginResetModel();
d->rebuildLayerList();
d->activeNode.clear();
if (d->layers.count() > 0)
{
d->nodeManager->slotUiActivatedNode(d->layers.at(0));
currentNodeChanged(d->layers.at(0));
}
emit countChanged();
endResetModel();
}
void LayerModel::notifyImageDeleted()
{
}
void LayerModel::nodeChanged(KisNodeSP node)
{
QModelIndex index = createIndex(d->layers.indexOf(node), 0);
dataChanged(index, index);
}
void LayerModel::imageChanged()
{
d->imageChangedTimer->start();
}
void LayerModel::imageHasChanged()
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::aboutToRemoveNode(KisNodeSP node)
{
Q_UNUSED(node)
QTimer::singleShot(0, this, SLOT(source_modelReset()));
}
void LayerModel::emitActiveChanges()
{
emit activeFilterConfigChanged();
emit activeNameChanged();
emit activeTypeChanged();
emit activeCompositeOpChanged();
emit activeOpacityChanged();
emit activeVisibleChanged();
emit activeLockedChanged();
emit activeRChannelActiveChanged();
emit activeGChannelActiveChanged();
emit activeBChannelActiveChanged();
emit activeAChannelActiveChanged();
emit activeRChannelLockedChanged();
emit activeGChannelLockedChanged();
emit activeBChannelLockedChanged();
emit activeAChannelLockedChanged();
}
QString LayerModel::activeName() const
{
if (d->activeNode.isNull())
return QString();
return d->activeNode->name();
}
void LayerModel::setActiveName(QString newName)
{
if (d->activeNode.isNull())
return;
d->activeNode->setName(newName);
emit activeNameChanged();
}
QString LayerModel::activeType() const
{
return d->activeNode->metaObject()->className();
}
int LayerModel::activeCompositeOp() const
{
if (d->activeNode.isNull())
return 0;
KoID entry(d->activeNode->compositeOp()->id());
QModelIndex idx = KisCompositeOpListModel::sharedInstance()->indexOf(entry);
if (idx.isValid())
return idx.row();
return 0;
}
void LayerModel::setActiveCompositeOp(int newOp)
{
if (d->activeNode.isNull())
return;
KoID entry;
if (KisCompositeOpListModel::sharedInstance()->entryAt(entry, KisCompositeOpListModel::sharedInstance()->index(newOp)))
{
d->activeNode->setCompositeOp(entry.id());
d->activeNode->setDirty();
emit activeCompositeOpChanged();
}
}
int LayerModel::activeOpacity() const
{
if (d->activeNode.isNull())
return 0;
return d->activeNode->opacity();
}
void LayerModel::setActiveOpacity(int newOpacity)
{
d->activeNode->setOpacity(newOpacity);
d->activeNode->setDirty();
emit activeOpacityChanged();
}
bool LayerModel::activeVisible() const
{ if (d->activeNode.isNull())
return false;
return d->activeNode->visible();
}
void LayerModel::setActiveVisibile(bool newVisible)
{
if (d->activeNode.isNull())
return;
setVisible(d->layers.indexOf(d->activeNode), newVisible);
emit activeVisibleChanged();
}
bool LayerModel::activeLocked() const
{
if (d->activeNode.isNull())
return false;
return d->activeNode->userLocked();
}
void LayerModel::setActiveLocked(bool newLocked)
{
if (d->activeNode.isNull())
return;
d->activeNode->setUserLocked(newLocked);
emit activeLockedChanged();
}
bool LayerModel::activeAChannelActive() const
{
KisLayer* layer = qobject_cast<KisLayer*>(d->activeNode.data());
bool state = false;
if (layer)
state = !layer->alphaChannelDisabled();
return state;
}
void LayerModel::setActiveAChannelActive(bool newActive)
{
KisLayer* layer = qobject_cast<KisLayer*>(d->activeNode.data());
if (layer)
{
layer->disableAlphaChannel(!newActive);
layer->setDirty();
emit activeAChannelActiveChanged();
}
}
bool LayerModel::activeAChannelLocked() const
{
KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(d->activeNode.data());
bool state = false;
if (layer)
state = layer->alphaLocked();
return state;
}
void LayerModel::setActiveAChannelLocked(bool newLocked)
{
KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(d->activeNode.data());
if (layer)
{
layer->setAlphaLocked(newLocked);
emit activeAChannelLockedChanged();
}
}
bool getActiveChannel(KisNodeSP node, int channelIndex)
{
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
bool flag = false;
if (layer)
{
QBitArray flags = layer->channelFlags();
if (channelIndex < flags.size()) {
flag = flags[channelIndex];
}
}
return flag;
}
bool getLockedChannel(KisNodeSP node, int channelIndex)
{
KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(node.data());
bool flag = false;
if (layer) {
QBitArray flags = layer->channelLockFlags();
flags = flags.isEmpty() ? layer->colorSpace()->channelFlags(true, true) : flags;
flag = flags[channelIndex];
}
return flag;
}
void setChannelActive(KisNodeSP node, int channelIndex, bool newActive)
{
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
if (layer) {
QBitArray flags = layer->channelFlags();
flags.setBit(channelIndex, newActive);
layer->setChannelFlags(flags);
layer->setDirty();
}
}
void setChannelLocked(KisNodeSP node, int channelIndex, bool newLocked)
{
KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(node.data());
if (layer) {
QBitArray flags = layer->channelLockFlags();
flags = flags.isEmpty() ? layer->colorSpace()->channelFlags(true, true) : flags;
flags.setBit(channelIndex, newLocked);
layer->setChannelLockFlags(flags);
}
}
bool LayerModel::activeBChannelActive() const
{
return getActiveChannel(d->activeNode, 2);
}
void LayerModel::setActiveBChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 2, newActive);
emit activeBChannelActiveChanged();
}
bool LayerModel::activeBChannelLocked() const
{
return getLockedChannel(d->activeNode, 2);
}
void LayerModel::setActiveBChannelLocked(bool newLocked)
{
setChannelLocked(d->activeNode, 2, newLocked);
emit activeBChannelLockedChanged();
}
bool LayerModel::activeGChannelActive() const
{
return getActiveChannel(d->activeNode, 1);
}
void LayerModel::setActiveGChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 1, newActive);
emit activeGChannelActiveChanged();
}
bool LayerModel::activeGChannelLocked() const
{
return getLockedChannel(d->activeNode, 1);
}
void LayerModel::setActiveGChannelLocked(bool newLocked)
{
setChannelLocked(d->activeNode, 1, newLocked);
emit activeGChannelLockedChanged();
}
bool LayerModel::activeRChannelActive() const
{
return getActiveChannel(d->activeNode, 0);
}
void LayerModel::setActiveRChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 0, newActive);
emit activeRChannelActiveChanged();
}
bool LayerModel::activeRChannelLocked() const
{
return getLockedChannel(d->activeNode, 0);
}
void LayerModel::setActiveRChannelLocked(bool newLocked)
{
setChannelLocked(d->activeNode, 0, newLocked);
emit activeRChannelLockedChanged();
}
QObject* LayerModel::activeFilterConfig() const
{
QMap<QString, QVariant> props;
QString filterId;
KisFilterMask* filterMask = qobject_cast<KisFilterMask*>(d->activeNode.data());
if (filterMask) {
props = filterMask->filter()->getProperties();
filterId = filterMask->filter()->name();
}
else {
KisAdjustmentLayer* adjustmentLayer = qobject_cast<KisAdjustmentLayer*>(d->activeNode.data());
if (adjustmentLayer)
{
props = adjustmentLayer->filter()->getProperties();
filterId = adjustmentLayer->filter()->name();
}
}
PropertyContainer* config = new PropertyContainer(filterId, 0);
QMap<QString, QVariant>::const_iterator i;
for(i = props.constBegin(); i != props.constEnd(); ++i) {
config->setProperty(i.key().toLatin1(), i.value());
- //qDebug() << "Getting active config..." << i.key() << i.value();
+ //dbgKrita << "Getting active config..." << i.key() << i.value();
}
return config;
}
void LayerModel::setActiveFilterConfig(QObject* newConfig)
{
if (d->activeNode.isNull())
return;
PropertyContainer* config = qobject_cast<PropertyContainer*>(newConfig);
if (!config)
return;
- //qDebug() << "Attempting to set new config" << config->name();
+ //dbgKrita << "Attempting to set new config" << config->name();
KisFilterConfiguration* realConfig = d->filters.value(config->name())->factoryConfiguration(d->activeNode->original());
QMap<QString, QVariant>::const_iterator i;
for(i = realConfig->getProperties().constBegin(); i != realConfig->getProperties().constEnd(); ++i)
{
realConfig->setProperty(i.key(), config->property(i.key().toLatin1()));
- //qDebug() << "Creating config..." << i.key() << i.value();
+ //dbgKrita << "Creating config..." << i.key() << i.value();
}
// The following code causes sporadic crashes, and disabling causes leaks. So, leaks it must be, for now.
// The cause is the lack of a smart pointer interface for passing filter configs around
// Must be remedied, but for now...
// if (d->newConfig)
// delete(d->newConfig);
d->newConfig = realConfig;
//d->updateActiveLayerWithNewFilterConfigTimer->start();
updateActiveLayerWithNewFilterConfig();
}
void LayerModel::updateActiveLayerWithNewFilterConfig()
{
if (!d->newConfig)
return;
- //qDebug() << "Setting new config..." << d->newConfig->name();
+ //dbgKrita << "Setting new config..." << d->newConfig->name();
KisFilterMask* filterMask = qobject_cast<KisFilterMask*>(d->activeNode.data());
if (filterMask)
{
- //qDebug() << "Filter mask";
+ //dbgKrita << "Filter mask";
if (filterMask->filter() == d->newConfig)
return;
- //qDebug() << "Setting filter mask";
+ //dbgKrita << "Setting filter mask";
filterMask->setFilter(d->newConfig);
}
else
{
KisAdjustmentLayer* adjustmentLayer = qobject_cast<KisAdjustmentLayer*>(d->activeNode.data());
if (adjustmentLayer)
{
- //qDebug() << "Adjustment layer";
+ //dbgKrita << "Adjustment layer";
if (adjustmentLayer->filter() == d->newConfig)
return;
- //qDebug() << "Setting filter on adjustment layer";
+ //dbgKrita << "Setting filter on adjustment layer";
adjustmentLayer->setFilter(d->newConfig);
}
else
{
- //qDebug() << "UNKNOWN, BAIL OUT!";
+ //dbgKrita << "UNKNOWN, BAIL OUT!";
}
}
d->newConfig = 0;
d->activeNode->setDirty(d->activeNode->extent());
d->image->setModified();
QTimer::singleShot(100, this, SIGNAL(activeFilterConfigChanged()));
}
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index af7b822d2fd..84263f85a00 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -1,536 +1,538 @@
# Disable -Wswitch because of the extra definitions we here:
# kis_input_manager.cpp: In member function ‘virtual bool KisInputManager::eventFilter(QObject*, QEvent*)’:
# warning: case value ‘1001’ not in enumerated type ‘QEvent::Type’ [-Wswitch]
# warning: case value ‘1002’ not in enumerated type ‘QEvent::Type’ [-Wswitch]
if (CMAKE_COMPILER_IS_GNUCXX)
add_definitions(${KDE4_ENABLE_EXCEPTIONS} -Wno-switch)
else ()
kde_enable_exceptions()
endif ()
include_directories(
${CMAKE_SOURCE_DIR}/libs/pigment/colorprofiles
${CMAKE_CURRENT_SOURCE_DIR}/flake
${CMAKE_CURRENT_SOURCE_DIR}/pigment/resources
${CMAKE_CURRENT_SOURCE_DIR}/widgets/resources
${CMAKE_CURRENT_SOURCE_DIR}/ora
${CMAKE_SOURCE_DIR}/krita/image/metadata
${CMAKE_SOURCE_DIR}/krita/ui/qtsingleapplication
${CMAKE_SOURCE_DIR}/krita/ui/qtlockedfile
${EXIV2_INCLUDE_DIR}
${OCIO_INCLUDE_DIR})
#add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_painter_configuration.cpp
canvas/kis_perspective_grid_manager.cpp
canvas/kis_perspective_grid_decoration.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_qpainter_canvas_widget_factory.cpp
canvas/kis_infinity_manager.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/KisShortcutsEditorItem.cpp
dialogs/KisShortcutEditWidget.cpp
dialogs/KisShortcutsEditorDelegate.cpp
dialogs/KisShortcutsDialog.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
kis_aboutdata.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
kis_canvas_resource_provider.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
kis_config_notifier.cpp
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_custom_pattern.cc
kis_factory2.cc
kis_file_layer.cpp
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
kis_painting_assistants_manager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
kis_resource_server_provider.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
kis_view_plugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
kra/kis_kra_utils.cpp
kra/kis_kra_load_visitor.cpp
kra/kis_kra_loader.cpp
kra/kis_kra_save_visitor.cpp
kra/kis_kra_saver.cpp
kra/kis_kra_savexml_visitor.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_texture_tile_update_info.cpp
ora/kis_open_raster_stack_load_visitor.cpp
ora/kis_open_raster_stack_save_visitor.cpp
ora/ora_load_context.cc
ora/ora_save_context.cc
recorder/kis_node_query_path_editor.cc
recorder/kis_recorded_action_creator.cc
recorder/kis_recorded_action_creator_factory.cc
recorder/kis_recorded_action_creator_factory_registry.cc
recorder/kis_recorded_action_editor_factory.cc
recorder/kis_recorded_action_editor_factory_registry.cc
recorder/kis_recorded_filter_action_editor.cc
recorder/kis_recorded_filter_action_creator.cpp
recorder/kis_recorded_paint_action_editor.cc
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_recording_adapter.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
tool/kis_tool_rectangle_base.cpp
tool/kis_tool_polyline_base.cpp
tool/kis_tool_utils.cpp
tool/kis_resources_snapshot.cpp
tool/kis_smoothing_options.cpp
tool/strokes/freehand_stroke.cpp
tool/strokes/kis_painter_based_stroke_strategy.cpp
tool/strokes/kis_filter_stroke_strategy.cpp
widgets/kis_channelflags_widget.cpp
widgets/kis_cmb_composite.cc
widgets/kis_cmb_contour.cpp
widgets/kis_cmb_gradient.cpp
widgets/kis_paintop_list_widget.cpp
widgets/kis_cmb_idlist.cc
widgets/kis_color_space_selector.cc
widgets/kis_advanced_color_space_selector.cc
widgets/kis_cie_tongue_widget.cpp
widgets/kis_curve_widget.cpp
widgets/kis_custom_image_widget.cc
widgets/kis_image_from_clipboard_widget.cpp
widgets/kis_double_widget.cc
widgets/kis_filter_selector_widget.cc
widgets/kis_gradient_chooser.cc
widgets/kis_gradient_slider_widget.cc
widgets/kis_gradient_slider.cpp
widgets/kis_iconwidget.cc
widgets/kis_mask_widgets.cpp
widgets/kis_meta_data_merge_strategy_chooser_widget.cc
widgets/kis_multi_bool_filter_widget.cc
widgets/kis_multi_double_filter_widget.cc
widgets/kis_multi_integer_filter_widget.cc
widgets/kis_multipliers_double_slider_spinbox.cpp
widgets/kis_paintop_presets_popup.cpp
widgets/kis_tool_options_popup.cpp
widgets/kis_paintop_presets_chooser_popup.cpp
widgets/kis_pattern_chooser.cc
widgets/kis_popup_button.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_tree_view_popup.cc
widgets/kis_slider_spin_box.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/squeezedcombobox.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
input/kis_input_manager.cpp
+ input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_transaction_based_command.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisDocumentEntry.cpp
KisDocumentSectionDelegate.cpp
KisDocumentSectionToolTip.cpp
KisDocumentSectionView.cpp
KisFilterChain.cpp
KisFilterChainLink.cpp
KisFilterChainLinkList.cpp
KisImportExportFilter.cpp
KisFilterEdge.cpp
KisFilterEntry.cpp
KisFilterGraph.cpp
KisImportExportManager.cpp
KisImportExportManager_p.cpp
KisFilterVertex.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisRecentDocumentsPane.cpp
KisStartupDialog.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoStackAction.cpp
KisView.cpp
thememanager.cpp
kis_icon_utils.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
widgets/kis_url_requester.cpp
kis_md5_generator.cpp
+ KisApplicationArguments.cpp
)
if(WIN32)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
)
endif()
if(UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support.cpp
qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_x11.cpp
)
endif()
endif()
kde_enable_exceptions()
if(WIN32)
#ki18n_wrap_ui(
# input/wintab/kis_screen_size_choice_dialog.ui
#)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggridsettings.ui
forms/wdggenerators.ui
forms/wdgcustompalette.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgpaintactioneditor.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/KisShortcutsDialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
forms/wdg_url_requester.ui
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisDocumentSectionPropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::GlobalAccel KF5::KDELibs4Support)
if (NOT WIN32)
target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
endif ()
if(GHNS)
target_link_libraries(kritaui ${KDE4_KFILE_LIBRARY} kritacolor kritaimage kritalibbrush kowidgets kowidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} KF5::NewStuff)
else()
target_link_libraries(kritaui ${KDE4_KFILE_LIBRARY} kritacolor kritaimage kritalibbrush kowidgets kowidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES})
endif ()
if(HAVE_OPENGL)
target_link_libraries(kritaui ${OPENGL_LIBRARIES} Qt5::OpenGL ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
set (GL_INTERFACE_LIBRARIES ";${OPENGL_LIBRARIES};Qt5::OpenGL")
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_link_libraries(kritaui LINK_INTERFACE_LIBRARIES kritaimage kritalibbrush pigmentcms KF5::KDELibs4Support ${GL_INTERFACE_LIBRARIES} )
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
########### install files ###############
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
install( FILES
kritaui_export.h
canvas/kis_canvas2.h
canvas/kis_canvas_decoration.h
canvas/kis_coordinates_converter.h
tool/kis_tool.h
kis_canvas_resource_provider.h
kis_cursor.h
KisViewManager.h
kis_ui_types.h
# kis_cmb_composite.h
# kis_cmb_idlist.h
# kis_color_cup.h
# kis_config.h
# kis_double_click_event.h
# kis_double_widget.h
# kis_filter_manager.h
# kis_gradient_chooser.h
# kis_gradient_slider_widget.h
# kis_histogram_view.h
# kis_icon_item.h
# kis_iconwidget.h
# kis_itemchooser.h
# kis_label_zoom.h
# kis_move_event.h
widgets/kis_multi_bool_filter_widget.h
widgets/kis_multi_double_filter_widget.h
widgets/kis_multi_integer_filter_widget.h
# kis_paintop_box.h
# kis_previewwidget.h
# kis_tool_non_paint.h
# kis_tool_paint.h
# kis_tool_freehand.h
# kis_tool_dummy.h
# kis_tool_manager.h
# kis_tool_types.h
# KoInputDevice.h
# canvas/kis_perspective_grid_manager.h
kis_paintop_option.h
kis_paintop_options_model.h
kis_paintop_settings_widget.h
processing/fill_processing_visitor.h
DESTINATION ${INCLUDE_INSTALL_DIR}/krita)
diff --git a/krita/ui/KisApplication.cpp b/krita/ui/KisApplication.cpp
index ccdaa198db3..d232d310c1b 100644
--- a/krita/ui/KisApplication.cpp
+++ b/krita/ui/KisApplication.cpp
@@ -1,661 +1,659 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2009 Thomas Zander <zander@kde.org>
Copyright (C) 2012 Boudewijn Rempt <boud@valdyas.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisApplication.h"
#include <KoPluginLoader.h>
#include <KoShapeRegistry.h>
-
#include <KoDpi.h>
#include "KoGlobal.h"
#include "KoConfig.h"
+#include <KoHashGeneratorProvider.h>
+#include <KoIcon.h>
#include <kcrash.h>
#include <klocale.h>
#include <kcmdlineargs.h>
#include <kdesktopfile.h>
#include <QMessageBox>
#include <kstandarddirs.h>
#include <kiconloader.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kmimetype.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <krecentdirs.h>
#include <QFile>
#include <QWidget>
#include <QSysInfo>
#include <QStringList>
#include <QDesktopServices>
#include <QProcessEnvironment>
#include <QDir>
#include <QDesktopWidget>
#include <stdlib.h>
#ifdef Q_OS_WIN
#include <windows.h>
#include <tchar.h>
#endif
#include "KisPrintJob.h"
#include "KisDocumentEntry.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "kis_factory2.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
+#include "kis_md5_generator.h"
#include "kis_config.h"
#include "flake/kis_shape_selection.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator_registry.h>
#include <generator/kis_generator.h>
#include <kis_paintop_registry.h>
#include <metadata/kis_meta_data_io_backend.h>
#include "kisexiv2/kis_exiv2.h"
#ifdef HAVE_OPENGL
#include "opengl/kis_opengl.h"
#endif
-KisApplication* KisApplication::KoApp = 0;
+#include <calligraversion.h>
+#include <calligragitversion.h>
+#include "KisApplicationArguments.h"
namespace {
const QTime appStartTime(QTime::currentTime());
}
class KisApplicationPrivate
{
public:
KisApplicationPrivate()
: splashScreen(0)
{}
QWidget *splashScreen;
};
class KisApplication::ResetStarting
{
public:
ResetStarting(QWidget *splash = 0)
: m_splash(splash)
{
}
~ResetStarting() {
if (m_splash) {
KConfigGroup cfg(KGlobal::config(), "SplashScreen");
bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
if (hideSplash) {
m_splash->hide();
}
else {
m_splash->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
QRect r(QPoint(), m_splash->size());
m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center());
m_splash->setWindowTitle(qAppName());
foreach(QObject *o, m_splash->children()) {
QWidget *w = qobject_cast<QWidget*>(o);
if (w && w->isHidden()) {
w->setVisible(true);
}
}
m_splash->show();
}
}
}
QWidget *m_splash;
};
-KisApplication::KisApplication(const QString &key)
- : QtSingleApplication(key, KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv())
+KisApplication::KisApplication(const QString &key, int &argc, char **argv)
+ : QtSingleApplication(key, argc, argv)
, d(new KisApplicationPrivate)
{
- KisApplication::KoApp = this;
+ setApplicationDisplayName("Krita");
+ setApplicationName("krita");
+
+ QString calligraVersion(CALLIGRA_VERSION_STRING);
+ QString version;
+#ifdef CALLIGRA_GIT_SHA1_STRING
+ QString gitVersion(CALLIGRA_GIT_SHA1_STRING);
+ version = QString("%1 (git %2)").arg(calligraVersion).arg(gitVersion);
+#else
+ version = calligraVersion;
+#endif
+ setApplicationVersion(version);
// Tell the iconloader about share/apps/calligra/icons
KIconLoader::global()->addAppDir("calligra");
// Initialize all Calligra directories etc.
KoGlobal::initialize();
+ // for cursors
+ KGlobal::dirs()->addResourceType("kis_pics", "data", "krita/pics/");
+
+ // for images in the paintop box
+ KGlobal::dirs()->addResourceType("kis_images", "data", "krita/images/");
+
+ KGlobal::dirs()->addResourceType("icc_profiles", "data", "krita/profiles/");
+
+ // Tell the iconloader about share/apps/calligra/icons
+ KIconLoader::global()->addAppDir("calligra");
+
+ setWindowIcon(koIcon("calligrakrita"));
+
+
#ifdef HAVE_OPENGL
KisOpenGL::initialize();
#endif
#ifdef Q_OS_MACX
if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 )
{
// fix Mac OS X 10.9 (mavericks) font issue
// https://bugreports.qt-project.org/browse/QTBUG-32789
QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
}
setAttribute(Qt::AA_DontShowIconsInMenus, true);
#endif
if (applicationName() == "krita" && qgetenv("KDE_FULL_SESSION").isEmpty()) {
// There are two themes that work for Krita, oxygen and plastique. Try to set plastique first, then oxygen
setStyle("Plastique");
setStyle("Breeze");
setStyle("Oxygen");
setStyle("Fusion");
}
+ KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
}
#if defined(Q_OS_WIN) && defined(ENV32BIT)
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL isWow64()
{
BOOL bIsWow64 = FALSE;
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(NULL != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//handle error
}
}
return bIsWow64;
}
#endif
-bool KisApplication::createNewDocFromTemplate(KCmdLineArgs *args, int argNumber, KisMainWindow *mainWindow)
+bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
{
QString templatePath;
- const KUrl templateUrl = args->url(argNumber);
- if (templateUrl.isLocalFile() && QFile::exists(templateUrl.toLocalFile())) {
+ const KUrl templateUrl = fileName;
+ if (QFile::exists(fileName)) {
templatePath = templateUrl.toLocalFile();
- kDebug(30003) << "using full path...";
- } else {
- QString desktopName(args->arg(argNumber));
+ dbgUI << "using full path...";
+ }
+ else {
+ QString desktopName(fileName);
const QString templatesResourcePath = KisPart::instance()->templatesResourcePath();
QStringList paths = KGlobal::dirs()->findAllResources("data", templatesResourcePath + "*/" + desktopName);
if (paths.isEmpty()) {
paths = KGlobal::dirs()->findAllResources("data", templatesResourcePath + desktopName);
}
if (paths.isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("No template found for: %1", desktopName));
} else if (paths.count() > 1) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Too many templates found for: %1", desktopName));
} else {
templatePath = paths.at(0);
}
}
if (!templatePath.isEmpty()) {
KUrl templateBase;
templateBase.setPath(templatePath);
KDesktopFile templateInfo(templatePath);
QString templateName = templateInfo.readUrl();
KUrl templateURL;
templateURL.setPath(templateBase.directory() + '/' + templateName);
KisDocument *doc = KisPart::instance()->createDocument();
if (mainWindow->openDocumentInternal(templateURL, doc)) {
doc->resetURL();
doc->setEmpty();
doc->setTitleModified();
- kDebug(30003) << "Template loaded...";
+ dbgUI << "Template loaded...";
return true;
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Template %1 failed to load.", templateURL.prettyUrl()));
}
}
return false;
}
void KisApplication::clearConfig()
{
KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
KSharedConfigPtr config = KGlobal::config();
// find user settings file
bool createDir = false;
QString kritarcPath = KStandardDirs::locateLocal("config", "kritarc", createDir);
QFile configFile(kritarcPath);
if (configFile.exists()) {
// clear file
if (configFile.open(QFile::WriteOnly)) {
configFile.close();
}
else {
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Failed to clear %1\n\n"
"Please make sure no other program is using the file and try again.",
kritarcPath),
QMessageBox::Ok, QMessageBox::Ok);
}
}
// reload from disk; with the user file settings cleared,
// this should load any default configuration files shipping with the program
config->reparseConfiguration();
config->sync();
}
void KisApplication::askClearConfig()
{
Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier);
if (askClearConfig) {
bool ok = QMessageBox::question(0,
i18nc("@title:window", "Krita"),
i18n("Do you want to clear the settings file?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
if (ok) {
clearConfig();
}
}
}
-bool KisApplication::start()
+bool KisApplication::start(const KisApplicationArguments &args)
{
#if defined(Q_OS_WIN) || defined (Q_OS_MACX)
#ifdef ENV32BIT
KisConfig cfg;
if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You are running a 32 bits build on a 64 bits Windows.\n"
"This is not recommended.\n"
"Please download and install the x64 build instead."));
cfg.writeEntry("WarnedAbout32Bits", true);
}
#endif
QDir appdir(applicationDirPath());
appdir.cdUp();
KGlobal::dirs()->addXdgDataPrefix(appdir.absolutePath() + "/share");
KGlobal::dirs()->addPrefix(appdir.absolutePath());
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
// If there's no kdehome, set it and restart the process.
if (!env.contains("KDEHOME")) {
qputenv("KDEHOME", QFile::encodeName(QDesktopServices::storageLocation(QDesktopServices::DataLocation)));
}
if (!env.contains("XDG_DATA_DIRS")) {
qputenv("XDG_DATA_DIRS", QFile::encodeName(appdir.absolutePath() + "/share"));
}
if (!env.contains("KDEDIR")) {
qputenv("KDEDIR", QFile::encodeName(appdir.absolutePath()));
}
if (!env.contains("KDEDIRS")) {
qputenv("KDEDIRS", QFile::encodeName(appdir.absolutePath()));
}
qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";"
+ appdir.absolutePath() + "/lib" + ";"
+ appdir.absolutePath() + "/lib/kde4" + ";"
+ appdir.absolutePath() + "/Frameworks" + ";"
+ appdir.absolutePath()));
#endif
- // Get the command line arguments which we have to parse
- KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
- int argsCount = args->count();
-
- QString dpiValues = args->getOption("dpi");
- if (!dpiValues.isEmpty()) {
- int sep = dpiValues.indexOf(QRegExp("[x, ]"));
- int dpiX;
- int dpiY = 0;
- bool ok = true;
- if (sep != -1) {
- dpiY = dpiValues.mid(sep + 1).toInt(&ok);
- dpiValues.truncate(sep);
- }
- if (ok) {
- dpiX = dpiValues.toInt(&ok);
- if (ok) {
- if (!dpiY) dpiY = dpiX;
- KoDpi::setDPI(dpiX, dpiY);
- }
- }
+ int dpiX = args.dpiX();
+ int dpiY = args.dpiY();
+ if (dpiX > 0 && dpiY > 0) {
+ KoDpi::setDPI(dpiX, dpiY);
}
- const bool doTemplate = args->isSet("template");
- const bool print = args->isSet("print");
- const bool exportAs = args->isSet("export");
- const bool exportAsPdf = args->isSet("export-pdf");
- const QString exportFileName = args->getOption("export-filename");
- const QString profileFileName = args->getOption("profile-filename");
+ const bool doTemplate = args.doTemplate();
+ const bool print = args.print();
+ const bool exportAs = args.exportAs();
+ const bool exportAsPdf = args.exportAsPdf();
+ const QString exportFileName = args.exportFileName();
+ const QString profileFileName = args.profileFileName();
const bool batchRun = (print || exportAs || exportAsPdf);
// print & exportAsPdf do user interaction ATM
const bool needsMainWindow = !exportAs;
// only show the mainWindow when no command-line mode option is passed
// TODO: fix print & exportAsPdf to work without mainwindow shown
const bool showmainWindow = !exportAs; // would be !batchRun;
const bool runningInGnome = (qgetenv("XDG_CURRENT_DESKTOP") == "GNOME");
const bool showSplashScreen = !batchRun && !runningInGnome;
if (d->splashScreen && showSplashScreen) {
d->splashScreen->show();
d->splashScreen->repaint();
processEvents();
}
ResetStarting resetStarting(d->splashScreen); // remove the splash when done
Q_UNUSED(resetStarting);
// Load various global plugins
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
// Load the krita-specific tools
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// Load dockers
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// XXX_EXIV: make the exiv io backends real plugins
KisExiv2::initialize();
KisMainWindow *mainWindow = 0;
if (needsMainWindow) {
// show a mainWindow asap, if we want that
mainWindow = KisPart::instance()->createMainWindow();
if (showmainWindow) {
mainWindow->show();
}
}
short int numberOfOpenDocuments = 0; // number of documents open
// Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf)
QList<KUrl> urls = checkAutosaveFiles();
if (!batchRun && mainWindow) {
foreach(const KUrl &url, urls) {
KisDocument *doc = KisPart::instance()->createDocument();
mainWindow->openDocumentInternal(url, doc);
}
}
+ // Get the command line arguments which we have to parse
+ int argsCount = args.filenames().count();
if (argsCount > 0) {
QTextStream profileoutput;
QFile profileFile(profileFileName);
if (!profileFileName.isEmpty()
&& profileFile.open(QFile::WriteOnly | QFile::Truncate)) {
profileoutput.setDevice(&profileFile);
}
// Loop through arguments
short int nPrinted = 0;
for (int argNumber = 0; argNumber < argsCount; argNumber++) {
- KUrl url = args->url(argNumber);
+ QString fileName = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
// called in mix with batch options? ignore and silently skip
if (batchRun) {
continue;
}
- if (createNewDocFromTemplate(args, argNumber, mainWindow)) {
+ if (createNewDocFromTemplate(fileName, mainWindow)) {
++numberOfOpenDocuments;
}
- // now try to load
+ // now try to load
}
else {
if (exportAs) {
KMimeType::Ptr outputMimetype;
outputMimetype = KMimeType::findByUrl(exportFileName, 0, false, true /* file doesn't exist */);
if (outputMimetype->name() == KMimeType::defaultMimeType()) {
- kError() << i18n("Mimetype not found, try using the -mimetype option") << endl;
+ dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
return 1;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QString outputFormat = outputMimetype->name();
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
- KisImportExportManager manager(url.path());
+ KisImportExportManager manager(fileName);
manager.setBatchMode(true);
QByteArray mime(outputFormat.toLatin1());
status = manager.exportDocument(exportFileName, mime);
if (status != KisImportExportFilter::OK) {
- kError() << "Could not export " << url.path() << "to" << exportFileName << ":" << (int)status;
+ dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << (int)status;
}
nPrinted++;
QTimer::singleShot(0, this, SLOT(quit()));
- } else if (mainWindow) {
+ }
+ else if (mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
- if (mainWindow->openDocumentInternal(url, doc)) {
+ if (mainWindow->openDocumentInternal(fileName, doc)) {
if (print) {
mainWindow->slotFilePrint();
nPrinted++;
// TODO: trigger closing of app once printing is done
}
else if (exportAsPdf) {
KisPrintJob *job = mainWindow->exportToPdf(exportFileName);
if (job)
connect (job, SIGNAL(destroyed(QObject*)), mainWindow,
SLOT(slotFileQuit()), Qt::QueuedConnection);
nPrinted++;
} else {
// Normal case, success
numberOfOpenDocuments++;
}
} else {
// .... if failed
// delete doc; done by openDocument
}
}
}
}
if (batchRun) {
return nPrinted > 0;
}
}
-
- args->clear();
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
delete d;
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = splashScreen;
}
-QStringList KisApplication::mimeFilter(KisImportExportManager::Direction direction) const
-{
- return KisImportExportManager::mimeFilter(KIS_MIME_TYPE,
- direction,
- KisDocumentEntry::extraNativeMimeTypes());
-}
-
-
bool KisApplication::notify(QObject *receiver, QEvent *event)
{
try {
return QApplication::notify(receiver, event);
} catch (std::exception &e) {
qWarning("Error %s sending event %i to object %s",
e.what(), event->type(), qPrintable(receiver->objectName()));
} catch (...) {
qWarning("Error <unknown> sending event %i to object %s",
event->type(), qPrintable(receiver->objectName()));
}
return false;
}
-KisApplication *KisApplication::koApplication()
-{
- return KoApp;
-}
-void KisApplication::remoteArguments(const QByteArray &message, QObject *socket)
+void KisApplication::remoteArguments(QByteArray &message, QObject *socket)
{
Q_UNUSED(socket);
// check if we have any mainwindow
KisMainWindow *mw = qobject_cast<KisMainWindow*>(qApp->activeWindow());
if (!mw) {
mw = KisPart::instance()->mainWindows().first();
}
if (!mw) {
return;
}
- QDataStream ds(message);
- KCmdLineArgs::loadAppArgs(ds);
-
- KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
- const bool doTemplate = args->isSet("template");
- const int argsCount = args->count();
+ KisApplicationArguments args = KisApplicationArguments::deserialize(message);
+ const bool doTemplate = args.doTemplate();
+ const int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; ++argNumber) {
- KUrl url = args->url(argNumber);
+ QString filename = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
- createNewDocFromTemplate(args, argNumber, mw);
+ createNewDocFromTemplate(filename, mw);
}
- else if (url.isValid()) {
+ else if (QFile(filename).exists()) {
KisDocument *doc = KisPart::instance()->createDocument();
- mw->openDocumentInternal(url, doc);
+ mw->openDocumentInternal(filename, doc);
}
}
}
-
- args->clear();
}
void KisApplication::fileOpenRequested(const QString &url)
{
KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
if (mainWindow) {
KisDocument *doc = KisPart::instance()->createDocument();
mainWindow->openDocumentInternal(url, doc);
}
}
QList<KUrl> KisApplication::checkAutosaveFiles()
{
// Check for autosave files from a previous run. There can be several, and
// we want to offer a restore for every one. Including a nice thumbnail!
QStringList autoSaveFiles;
QStringList filters;
filters << QString(".krita-*-*-autosave.kra");
#ifdef Q_OS_WIN
QDir dir = QDir::temp();
#else
QDir dir = QDir::home();
#endif
// all autosave files for our application
autoSaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
// Allow the user to make their selection
if (autoSaveFiles.size() > 0) {
KisAutoSaveRecoveryDialog *dlg = new KisAutoSaveRecoveryDialog(autoSaveFiles, activeWindow());
if (dlg->exec() == QDialog::Accepted) {
QStringList filesToRecover = dlg->recoverableFiles();
foreach (const QString &autosaveFile, autoSaveFiles) {
if (!filesToRecover.contains(autosaveFile)) {
QFile::remove(dir.absolutePath() + "/" + autosaveFile);
}
}
autoSaveFiles = filesToRecover;
}
else {
// don't recover any of the files, but don't delete them either
autoSaveFiles.clear();
}
}
QList<KUrl> autosaveUrls;
if (autoSaveFiles.size() > 0) {
foreach(const QString &autoSaveFile, autoSaveFiles) {
KUrl url;
url.setPath(dir.absolutePath() + "/" + autoSaveFile);
autosaveUrls << url;
}
}
return autosaveUrls;
}
diff --git a/krita/ui/KisApplication.h b/krita/ui/KisApplication.h
index d1be826c971..85bb90b0ef3 100644
--- a/krita/ui/KisApplication.h
+++ b/krita/ui/KisApplication.h
@@ -1,133 +1,111 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_APPLICATION_H
#define KIS_APPLICATION_H
#include <qtsingleapplication/qtsingleapplication.h>
#include "kritaui_export.h"
class KisMainWindow;
class KisApplicationPrivate;
class KCmdLineArgs;
class QWidget;
class QStringList;
+class QCommandLineParser;
+class KisApplicationArguments;
#include <KisImportExportManager.h>
-#define koApp KisApplication::koApplication()
-
/**
* @brief Base class for all %Calligra apps
*
* This class handles arguments given on the command line and
* shows a generic about dialog for all Calligra apps.
*
* In addition it adds the standard directories where Calligra applications
* can find their images etc.
*
* If the last mainwindow becomes closed, KisApplication automatically
* calls QApplication::quit.
*/
class KRITAUI_EXPORT KisApplication : public QtSingleApplication
{
Q_OBJECT
public:
/**
* Creates an application object, adds some standard directories and
* initializes kimgio.
*/
- explicit KisApplication(const QString &key);
+ explicit KisApplication(const QString &key, int &argc, char **argv);
/**
* Destructor.
*/
virtual ~KisApplication();
/**
* Call this to start the application.
*
* Parses command line arguments and creates the initial main windowss and docs
* from them (or an empty doc if no cmd-line argument is specified ).
*
* You must call this method directly before calling QApplication::exec.
*
* It is valid behaviour not to call this method at all. In this case you
* have to process your command line parameters by yourself.
*/
- virtual bool start();
+ virtual bool start(const KisApplicationArguments &args);
/**
* Checks if user is holding ctrl+alt+shift keys and asks if the settings file should be cleared.
*
* Typically called during startup before reading the config.
*/
void askClearConfig();
/**
* Tell KisApplication to show this splashscreen when you call start();
* when start returns, the splashscreen is hidden. Use KSplashScreen
* to have the splash show correctly on Xinerama displays.
*/
void setSplashScreen(QWidget *splash);
- /**
- * return a list of mimetypes this application supports.
- */
- QStringList mimeFilter(KisImportExportManager::Direction direction) const;
-
/// Overridden to handle exceptions from event handlers.
bool notify(QObject *receiver, QEvent *event);
- /**
- * Returns the current application object.
- *
- * This is similar to the global QApplication pointer qApp. It
- * allows access to the single global KisApplication object, since
- * more than one cannot be created in the same application. It
- * saves you the trouble of having to pass the pointer explicitly
- * to every function that may require it.
- * @return the current application object
- */
- static KisApplication* koApplication();
-
-protected:
-
- // Current application object.
- static KisApplication *KoApp;
-
public Q_SLOTS:
- void remoteArguments(const QByteArray &message, QObject*socket);
+ void remoteArguments(QByteArray &message, QObject*socket);
void fileOpenRequested(const QString & url);
private:
/// @return the number of autosavefiles opened
QList<KUrl> checkAutosaveFiles();
- bool createNewDocFromTemplate(KCmdLineArgs *args, int argNumber, KisMainWindow *mainWindow);
+ bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow);
void clearConfig();
private:
KisApplicationPrivate * const d;
class ResetStarting;
friend class ResetStarting;
};
#endif
diff --git a/krita/ui/KisApplicationArguments.cpp b/krita/ui/KisApplicationArguments.cpp
new file mode 100644
index 00000000000..d58bbc0f996
--- /dev/null
+++ b/krita/ui/KisApplicationArguments.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "KisApplicationArguments.h"
+
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QApplication>
+#include <QStringList>
+#include <QString>
+#include <QDebug>
+#include <QDataStream>
+#include <QBuffer>
+
+#include <klocale.h>
+
+struct Q_DECL_HIDDEN KisApplicationArguments::Private
+{
+ Private()
+ : dpiX(0)
+ , dpiY(0)
+ , doTemplate(false)
+ , print(false)
+ , exportAs(false)
+ , exportAsPdf(false)
+ {
+ }
+
+ QStringList filenames;
+ int dpiX;
+ int dpiY;
+ bool doTemplate;
+ bool print;
+ bool exportAs;
+ bool exportAsPdf;
+ QString exportFileName;
+ QString profileFileName;
+
+};
+
+KisApplicationArguments::KisApplicationArguments(const QApplication &app)
+ : d(new Private)
+{
+ qDebug() << "Going to parse" << app.arguments();
+
+ QCommandLineParser parser;
+ parser.addVersionOption();
+ parser.addHelpOption();
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("print"), i18n("Only print and exit")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("template"), i18n("Open a new document with a template")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("dpi"), i18n("Override display DPI"), QLatin1String("dpiX,dpiY")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export-pdf"), i18n("Only export to PDF and exit")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export"), i18n("Export to the given filename and exit")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export-filename"), i18n("Filename for export/export-pdf"), QLatin1String("filename")));
+ parser.addOption(QCommandLineOption(QStringList() << QLatin1String("profile-filename"), i18n("Filename to write profiling information into."), QLatin1String("filename")));
+ parser.addPositionalArgument(QLatin1String("[file(s)]"), i18n("File(s) or URL(s) to open"));
+ parser.process(app);
+
+ d->filenames = parser.positionalArguments();
+
+ QString dpiValues = parser.value("dpi");
+ if (!dpiValues.isEmpty()) {
+ int sep = dpiValues.indexOf(QRegExp("[x, ]"));
+ bool ok = true;
+ if (sep != -1) {
+ d->dpiY = dpiValues.mid(sep + 1).toInt(&ok);
+ dpiValues.truncate(sep);
+ }
+ if (ok) {
+ d->dpiX = dpiValues.toInt(&ok);
+ if (ok) {
+ if (!d->dpiY)
+ d->dpiY = d->dpiX;
+ }
+ }
+ }
+ d->doTemplate = parser.isSet("template");
+ d->print = parser.isSet("print");
+ d->exportAs = parser.isSet("export");
+ d->exportAsPdf = parser.isSet("export-pdf");
+ d->exportFileName = parser.value("export-filename");
+ d->profileFileName = parser.value("profile-filename");
+
+ qDebug() << d->filenames;
+
+}
+
+KisApplicationArguments::KisApplicationArguments(const KisApplicationArguments &rhs)
+ : d(new Private)
+{
+ d->filenames = rhs.filenames();
+ d->dpiX = rhs.dpiX();
+ d->dpiY = rhs.dpiY();
+ d->doTemplate = rhs.doTemplate();
+ d->print = rhs.print();
+ d->exportAs = rhs.exportAs();
+ d->exportAsPdf = rhs.exportAsPdf();
+ d->exportFileName = rhs.exportFileName();
+ d->profileFileName = rhs.profileFileName();
+}
+
+KisApplicationArguments::~KisApplicationArguments()
+{
+}
+
+void KisApplicationArguments::operator=(const KisApplicationArguments &rhs)
+{
+ d->filenames = rhs.filenames();
+ d->dpiX = rhs.dpiX();
+ d->dpiY = rhs.dpiY();
+ d->doTemplate = rhs.doTemplate();
+ d->print = rhs.print();
+ d->exportAs = rhs.exportAs();
+ d->exportAsPdf = rhs.exportAsPdf();
+ d->exportFileName = rhs.exportFileName();
+ d->profileFileName = rhs.profileFileName();
+}
+
+QByteArray KisApplicationArguments::serialize()
+{
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QIODevice::WriteOnly);
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_5);
+ ds << d->filenames.count();
+ foreach(const QString &filename, d->filenames) {
+ ds << filename;
+ }
+ ds << d->dpiX;
+ ds << d->dpiY;
+ ds << d->doTemplate;
+ ds << d->print;
+ ds << d->exportAs;
+ ds << d->exportAsPdf;
+ ds << d->exportFileName;
+ ds << d->profileFileName;
+
+ buf.close();
+
+ return ba;
+}
+
+KisApplicationArguments KisApplicationArguments::deserialize(QByteArray &serialized)
+{
+ KisApplicationArguments args;
+
+ QBuffer buf(&serialized);
+ buf.open(QIODevice::ReadOnly);
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_5);
+ int count;
+ ds >> count;
+ for(int i = 0; i < count; ++i) {
+ QString s;
+ ds >> s;
+ args.d->filenames << s;
+ }
+ ds >> args.d->dpiX;
+ ds >> args.d->dpiY;
+ ds >> args.d->doTemplate;
+ ds >> args.d->print;
+ ds >> args.d->exportAs;
+ ds >> args.d->exportAsPdf;
+ ds >> args.d->exportFileName;
+ ds >> args.d->profileFileName;
+
+ buf.close();
+
+ return args;
+}
+
+QStringList KisApplicationArguments::filenames() const
+{
+ return d->filenames;
+}
+
+int KisApplicationArguments::dpiX() const
+{
+ return d->dpiX;
+}
+
+int KisApplicationArguments::dpiY() const
+{
+ return d->dpiY;
+}
+
+bool KisApplicationArguments::doTemplate() const
+{
+ return d->doTemplate;
+}
+
+bool KisApplicationArguments::print() const
+{
+ return d->print;
+}
+
+bool KisApplicationArguments::exportAs() const
+{
+ return d->exportAs;
+}
+
+bool KisApplicationArguments::exportAsPdf() const
+{
+ return d->exportAsPdf;
+}
+
+QString KisApplicationArguments::exportFileName() const
+{
+ return d->exportFileName;
+}
+
+QString KisApplicationArguments::profileFileName() const
+{
+ return d->profileFileName;
+}
+
+KisApplicationArguments::KisApplicationArguments()
+ : d(new Private)
+{
+}
diff --git a/krita/ui/KisApplicationArguments.h b/krita/ui/KisApplicationArguments.h
new file mode 100644
index 00000000000..161f41f2417
--- /dev/null
+++ b/krita/ui/KisApplicationArguments.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef KISAPPLICATIONARGUMENTS_H
+#define KISAPPLICATIONARGUMENTS_H
+
+#include <QScopedPointer>
+
+class QApplication;
+class QByteArray;
+class QStringList;
+
+#include "kritaui_export.h"
+
+class KRITAUI_EXPORT KisApplicationArguments
+{
+public:
+
+ KisApplicationArguments(const QApplication &app);
+ KisApplicationArguments(const KisApplicationArguments &rhs);
+ ~KisApplicationArguments();
+
+ void operator=(const KisApplicationArguments& rhs);
+ QByteArray serialize();
+ static KisApplicationArguments deserialize(QByteArray &serialized);
+
+ QStringList filenames() const;
+
+ int dpiX() const;
+ int dpiY() const;
+ bool doTemplate() const;
+ bool print() const;
+ bool exportAs() const;
+ bool exportAsPdf() const;
+ QString exportFileName() const;
+ QString profileFileName() const;
+
+private:
+
+ KisApplicationArguments();
+
+ struct Private;
+ const QScopedPointer<Private> d;
+};
+
+#endif // KISAPPLICATIONARGUMENTS_H
diff --git a/krita/ui/KisAutoSaveRecoveryDialog.cpp b/krita/ui/KisAutoSaveRecoveryDialog.cpp
index 078ef3ae0d2..3ade24a09bd 100644
--- a/krita/ui/KisAutoSaveRecoveryDialog.cpp
+++ b/krita/ui/KisAutoSaveRecoveryDialog.cpp
@@ -1,259 +1,259 @@
/* This file is part of the KDE project
Copyright (C) 2012 Boudewijn Rempt <boud@valdyas.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisAutoSaveRecoveryDialog.h"
#include <KoStore.h>
#include <kwidgetitemdelegate.h>
#include <klocale.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListView>
#include <QAbstractTableModel>
#include <QLabel>
#include <QDir>
#include <QFileInfo>
#include <QDateTime>
#include <QImage>
#include <QPixmap>
#include <QHeaderView>
#include <QStyledItemDelegate>
#include <QPainter>
#include <QCheckBox>
-#include <QDebug>
+#include <kis_debug.h>
struct FileItem {
FileItem() : checked(true) {}
QImage thumbnail;
QString name;
QString date;
bool checked;
};
class FileItemDelegate : public KWidgetItemDelegate
{
public:
FileItemDelegate(QAbstractItemView *itemView, KisAutoSaveRecoveryDialog *dlg)
: KWidgetItemDelegate(itemView, dlg)
, m_parent(dlg)
{
}
QList<QWidget*> createItemWidgets(const QModelIndex& index) const
{
// a lump of coal and a piece of elastic will get you through the world
QModelIndex idx = property("goya:creatingWidgetForIndex").value<QModelIndex>();
QWidget *page = new QWidget;
QHBoxLayout *layout = new QHBoxLayout(page);
QCheckBox *checkBox = new QCheckBox;
checkBox->setProperty("fileitem", idx.data());
connect(checkBox, SIGNAL(toggled(bool)), m_parent, SLOT(toggleFileItem(bool)));
QLabel *thumbnail = new QLabel;
QLabel *filename = new QLabel;
QLabel *dateModified = new QLabel;
layout->addWidget(checkBox);
layout->addWidget(thumbnail);
layout->addWidget(filename);
layout->addWidget(dateModified);
page->setFixedSize(600, 200);
return QList<QWidget*>() << page;
}
void updateItemWidgets(const QList<QWidget*> widgets,
const QStyleOptionViewItem &option,
const QPersistentModelIndex &index) const
{
FileItem *fileItem = (FileItem*)index.data().value<void*>();
QWidget* page= widgets[0];
QHBoxLayout* layout = qobject_cast<QHBoxLayout*>(page->layout());
QCheckBox *checkBox = qobject_cast<QCheckBox*>(layout->itemAt(0)->widget());
QLabel *thumbnail = qobject_cast<QLabel*>(layout->itemAt(1)->widget());
QLabel *filename = qobject_cast<QLabel*>(layout->itemAt(2)->widget());
QLabel *modified = qobject_cast<QLabel*>(layout->itemAt(3)->widget());
checkBox->setChecked(fileItem->checked);
thumbnail->setPixmap(QPixmap::fromImage(fileItem->thumbnail));
filename->setText(fileItem->name);
modified->setText(fileItem->date);
// move the page _up_ otherwise it will draw relative to the actual postion
page->setGeometry(option.rect.translated(0, -option.rect.y()));
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const
{
//paint background for selected or hovered item
QStyleOptionViewItemV4 opt = option;
itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, 0);
}
QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const
{
return QSize(600, 200);
}
KisAutoSaveRecoveryDialog *m_parent;
};
class KisAutoSaveRecoveryDialog::FileItemModel : public QAbstractListModel
{
public:
FileItemModel(QList<FileItem*> fileItems, QObject *parent)
: QAbstractListModel(parent)
, m_fileItems(fileItems){}
virtual ~FileItemModel()
{
qDeleteAll(m_fileItems);
m_fileItems.clear();
}
int rowCount(const QModelIndex &/*parent*/) const { return m_fileItems.size(); }
Qt::ItemFlags flags(const QModelIndex& /*index*/) const
{
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
return flags;
}
QVariant data(const QModelIndex& index, int role) const
{
if (index.isValid() && index.row() < m_fileItems.size()) {
FileItem *item = m_fileItems.at(index.row());
switch (role) {
case Qt::DisplayRole:
{
return QVariant::fromValue<void*>((void*)item);
}
case Qt::SizeHintRole:
return QSize(600, 200);
}
}
return QVariant();
}
bool setData(const QModelIndex& index, const QVariant& /*value*/, int role)
{
if (index.isValid() && index.row() < m_fileItems.size()) {
if (role == Qt::CheckStateRole) {
m_fileItems.at(index.row())->checked = !m_fileItems.at(index.row())->checked;
return true;
}
}
return false;
}
QList<FileItem *> m_fileItems;
};
KisAutoSaveRecoveryDialog::KisAutoSaveRecoveryDialog(const QStringList &filenames, QWidget *parent) :
KDialog(parent)
{
setCaption(i18nc("@title:window", "Recover Files"));
setMinimumSize(650, 500);
QWidget *page = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(page);
if (filenames.size() == 1) {
layout->addWidget(new QLabel(i18n("The following autosave file can be recovered:")));
}
else {
layout->addWidget(new QLabel(i18n("The following autosave files can be recovered:")));
}
m_listView = new QListView();
m_listView->setAcceptDrops(false);
KWidgetItemDelegate *delegate = new FileItemDelegate(m_listView, this);
m_listView->setItemDelegate(delegate);
QList<FileItem*> fileItems;
foreach(const QString &filename, filenames) {
FileItem *file = new FileItem();
file->name = filename;
#ifdef Q_OS_WIN
QString path = QDir::tempPath() + "/" + filename;
#else
QString path = QDir::homePath() + "/" + filename;
#endif
// get thumbnail -- all calligra apps save a thumbnail
KoStore* store = KoStore::createStore(path, KoStore::Read);
if (store && (store->open(QString("Thumbnails/thumbnail.png"))
|| store->open(QString("preview.png")))) {
// Hooray! No long delay for the user...
QByteArray bytes = store->read(store->size());
store->close();
delete store;
QImage img;
img.loadFromData(bytes);
file->thumbnail = img.scaled(QSize(200,200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
// get the date
QDateTime date = QFileInfo(path).lastModified();
file->date = "(" + date.toString(Qt::LocalDate) + ")";
fileItems.append(file);
}
m_model = new FileItemModel(fileItems, m_listView);
m_listView->setModel(m_model);
layout->addWidget(m_listView);
layout->addWidget(new QLabel(i18n("If you select Cancel, all recoverable files will be kept.\nIf you press OK, selected files will be recovered, the unselected files discarded.")));
setMainWidget(page);
}
QStringList KisAutoSaveRecoveryDialog::recoverableFiles()
{
QStringList files;
foreach(FileItem* fileItem, m_model->m_fileItems) {
if (fileItem->checked) {
files << fileItem->name;
}
}
return files;
}
void KisAutoSaveRecoveryDialog::toggleFileItem(bool toggle)
{
// I've made better man from a piece of putty and matchstick!
QVariant v = sender()->property("fileitem") ;
if (v.isValid()) {
FileItem *fileItem = (FileItem*)v.value<void*>();
fileItem->checked = toggle;
}
}
diff --git a/krita/ui/KisDocument.cpp b/krita/ui/KisDocument.cpp
index 497b108733e..430044b1c58 100644
--- a/krita/ui/KisDocument.cpp
+++ b/krita/ui/KisDocument.cpp
@@ -1,2641 +1,2642 @@
/* This file is part of the Krita project
*
* Copyright (C) 2014 Boudewijn Rempt <boud@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h" // XXX: remove
#include <QMessageBox> // XXX: remove
#include "KisApplication.h"
#include "KisDocument.h"
#include "KisImportExportManager.h"
#include "KisPart.h"
#include "KisView.h"
#include <KoCanvasBase.h>
#include <KoColor.h>
#include <KoColorProfile.h>
#include <KoColorSpaceEngine.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoDocumentInfoDlg.h>
#include <KoDocumentInfo.h>
#include <KoDpi.h>
#include <KoUnit.h>
#include <KoEmbeddedDocumentSaver.h>
#include <KoFileDialog.h>
#include <KoID.h>
#include <KoOdfReadStore.h>
#include <KoProgressProxy.h>
#include <KoProgressUpdater.h>
#include <KoSelection.h>
#include <KoShape.h>
#include <KoShapeController.h>
#include <KoStore.h>
#include <KoUpdater.h>
#include <KoXmlWriter.h>
#include <kmimetype.h>
#include <kfileitem.h>
#include <kio/netaccess.h>
#include <kio/job.h>
#include <klocale.h>
#include <ksavefile.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kstandarddirs.h>
#include <kdesktopfile.h>
#include <kconfiggroup.h>
#include <kfileitem.h>
#include <kfileitem.h>
#include <kdirnotify.h>
#include <QTemporaryFile>
#include "kundo2stack.h"
#include <QApplication>
#include <QBuffer>
#include <QDesktopServices>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>
#include <QFileInfo>
#include <QImage>
#include <QList>
#include <QPainter>
#include <QRect>
#include <QScopedPointer>
#include <QSize>
#include <QStringList>
#include <QtGlobal>
#include <QTimer>
#include <QWidget>
// Krita Image
#include <kis_config.h>
#include <flake/kis_shape_layer.h>
#include <kis_debug.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_name_server.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_selection.h>
#include <kis_fill_painter.h>
#include <kis_document_undo_store.h>
#include <kis_painting_assistants_decoration.h>
+#include <kis_debug.h>
// Local
#include "kis_factory2.h"
#include "KisViewManager.h"
#include "kis_clipboard.h"
#include "widgets/kis_custom_image_widget.h"
#include "canvas/kis_canvas2.h"
#include "flake/kis_shape_controller.h"
#include "kra/kis_kra_loader.h"
#include "kra/kis_kra_saver.h"
#include "kis_statusbar.h"
#include "widgets/kis_progress_widget.h"
#include "kis_canvas_resource_provider.h"
#include "kis_resource_server_provider.h"
#include "kis_node_manager.h"
#include "KisPart.h"
static const char CURRENT_DTD_VERSION[] = "2.0";
// Define the protocol used here for embedded documents' URL
// This used to "store" but KUrl didn't like it,
// so let's simply make it "tar" !
#define STORE_PROTOCOL "tar"
// The internal path is a hack to make KUrl happy and for document children
#define INTERNAL_PROTOCOL "intern"
#define INTERNAL_PREFIX "intern:/"
// Warning, keep it sync in koStore.cc
#include <unistd.h>
using namespace std;
/**********************************************************
*
* KisDocument
*
**********************************************************/
namespace {
class DocumentProgressProxy : public KoProgressProxy {
public:
KisMainWindow *m_mainWindow;
DocumentProgressProxy(KisMainWindow *mainWindow)
: m_mainWindow(mainWindow)
{
}
~DocumentProgressProxy() {
// signal that the job is done
setValue(-1);
}
int maximum() const {
return 100;
}
void setValue(int value) {
if (m_mainWindow) {
m_mainWindow->slotProgress(value);
}
}
void setRange(int /*minimum*/, int /*maximum*/) {
}
void setFormat(const QString &/*format*/) {
}
};
}
//static
QString KisDocument::newObjectName()
{
static int s_docIFNumber = 0;
QString name; name.setNum(s_docIFNumber++); name.prepend("document_");
return name;
}
class UndoStack : public KUndo2Stack
{
public:
UndoStack(KisDocument *doc)
: m_doc(doc)
{
}
void setIndex(int idx) {
KisImageWSP image = this->image();
image->requestStrokeCancellation();
if(image->tryBarrierLock()) {
KUndo2Stack::setIndex(idx);
image->unlock();
}
}
void notifySetIndexChangedOneCommand() {
KisImageWSP image = this->image();
image->unlock();
image->barrierLock();
}
void undo() {
KisImageWSP image = this->image();
image->requestUndoDuringStroke();
if(image->tryBarrierLock()) {
KUndo2Stack::undo();
image->unlock();
}
}
void redo() {
KisImageWSP image = this->image();
if(image->tryBarrierLock()) {
KUndo2Stack::redo();
image->unlock();
}
}
private:
KisImageWSP image() {
KisImageWSP currentImage = m_doc->image();
Q_ASSERT(currentImage);
return currentImage;
}
private:
KisDocument *m_doc;
};
class Q_DECL_HIDDEN KisDocument::Private
{
public:
Private(KisDocument *document) :
document(document),
// XXX: the part should _not_ be modified from the document
docInfo(0),
progressUpdater(0),
progressProxy(0),
profileStream(0),
filterManager(0),
specialOutputFlag(0), // default is native format
isImporting(false),
isExporting(false),
password(QString()),
modifiedAfterAutosave(false),
autosaving(false),
shouldCheckAutoSaveFile(true),
autoErrorHandlingEnabled(true),
backupFile(true),
backupPath(QString()),
doNotSaveExtDoc(false),
storeInternal(false),
isLoading(false),
undoStack(0),
modified(false),
readwrite(true),
disregardAutosaveFailure(false),
nserver(0),
macroNestDepth(0),
kraLoader(0)
{
m_job = 0;
m_statJob = 0;
m_uploadJob = 0;
m_saveOk = false;
m_waitForSave = false;
m_duringSaveAs = false;
m_bTemp = false;
m_bAutoDetectedMime = false;
confirmNonNativeSave[0] = true;
confirmNonNativeSave[1] = true;
if (KGlobal::locale()->measureSystem() == KLocale::Imperial) {
unit = KoUnit::Inch;
} else {
unit = KoUnit::Centimeter;
}
}
~Private() {
// Don't delete m_d->shapeController because it's in a QObject hierarchy.
delete nserver;
}
KisDocument *document;
KoDocumentInfo *docInfo;
KoProgressUpdater *progressUpdater;
KoProgressProxy *progressProxy;
QTextStream *profileStream;
QTime profileReferenceTime;
KoUnit unit;
KisImportExportManager *filterManager; // The filter-manager to use when loading/saving [for the options]
QByteArray mimeType; // The actual mimetype of the document
QByteArray outputMimeType; // The mimetype to use when saving
bool confirmNonNativeSave [2]; // used to pop up a dialog when saving for the
// first time if the file is in a foreign format
// (Save/Save As, Export)
int specialOutputFlag; // See KoFileDialog in koMainWindow.cc
bool isImporting;
bool isExporting; // File --> Import/Export vs File --> Open/Save
QString password; // The password used to encrypt an encrypted document
QTimer autoSaveTimer;
QString lastErrorMessage; // see openFile()
int autoSaveDelay; // in seconds, 0 to disable.
bool modifiedAfterAutosave;
bool autosaving;
bool shouldCheckAutoSaveFile; // usually true
bool autoErrorHandlingEnabled; // usually true
bool backupFile;
QString backupPath;
bool doNotSaveExtDoc; // makes it possible to save only internally stored child documents
bool storeInternal; // Store this doc internally even if url is external
bool isLoading; // True while loading (openUrl is async)
KUndo2Stack *undoStack;
KoGridData gridData;
KoGuidesData guidesData;
bool isEmpty;
KoPageLayout pageLayout;
KIO::FileCopyJob * m_job;
KIO::StatJob * m_statJob;
KIO::FileCopyJob * m_uploadJob;
KUrl m_originalURL; // for saveAs
QString m_originalFilePath; // for saveAs
bool m_saveOk : 1;
bool m_waitForSave : 1;
bool m_duringSaveAs : 1;
bool m_bTemp: 1; // If @p true, @p m_file is a temporary file that needs to be deleted later.
bool m_bAutoDetectedMime : 1; // whether the mimetype in the arguments was detected by the part itself
KUrl m_url; // Remote (or local) url - the one displayed to the user.
QString m_file; // Local file - the only one the part implementation should deal with.
QEventLoop m_eventLoop;
bool modified;
bool readwrite;
bool disregardAutosaveFailure;
KisNameServer *nserver;
qint32 macroNestDepth;
KisImageSP image;
KisNodeSP preActivatedNode;
KisShapeController* shapeController;
KoShapeController* koShapeController;
KisKraLoader* kraLoader;
KisKraSaver* kraSaver;
QList<KisPaintingAssistant*> assistants;
bool openFile()
{
DocumentProgressProxy *progressProxy = 0;
if (!document->progressProxy()) {
KisMainWindow *mainWindow = 0;
if (KisPart::instance()->mainWindows().count() > 0) {
mainWindow = KisPart::instance()->mainWindows()[0];
}
progressProxy = new DocumentProgressProxy(mainWindow);
document->setProgressProxy(progressProxy);
}
document->setUrl(m_url);
bool ok = document->openFile();
if (progressProxy) {
document->setProgressProxy(0);
delete progressProxy;
}
return ok;
}
bool openLocalFile()
{
m_bTemp = false;
// set the mimetype only if it was not already set (for example, by the host application)
if (mimeType.isEmpty()) {
// get the mimetype of the file
// using findByUrl() to avoid another string -> url conversion
KMimeType::Ptr mime = KMimeType::findByUrl(m_url, 0, true /* local file*/);
if (mime) {
mimeType = mime->name().toLocal8Bit();
m_bAutoDetectedMime = true;
}
}
const bool ret = openFile();
if (ret) {
emit document->completed();
} else {
emit document->canceled(QString());
}
return ret;
}
void openRemoteFile()
{
m_bTemp = true;
// Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
QString fileName = m_url.fileName();
QFileInfo fileInfo(fileName);
QString ext = fileInfo.completeSuffix();
QString extension;
if (!ext.isEmpty() && m_url.query().isNull()) // not if the URL has a query, e.g. cgi.pl?something
extension = '.'+ext; // keep the '.'
QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + extension);
tempFile.setAutoRemove(false);
tempFile.open();
m_file = tempFile.fileName();
KUrl destURL;
destURL.setPath( m_file );
KIO::JobFlags flags = KIO::DefaultFlags;
flags |= KIO::Overwrite;
m_job = KIO::file_copy(m_url, destURL, 0600, flags);
QObject::connect(m_job, SIGNAL(result(KJob*)), document, SLOT(_k_slotJobFinished(KJob*)));
QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*,QString)), document, SLOT(_k_slotGotMimeType(KIO::Job*,QString)));
}
// Set m_file correctly for m_url
void prepareSaving()
{
// Local file
if ( m_url.isLocalFile() )
{
if ( m_bTemp ) // get rid of a possible temp file first
{ // (happens if previous url was remote)
QFile::remove( m_file );
m_bTemp = false;
}
m_file = m_url.toLocalFile();
}
else
{ // Remote file
// We haven't saved yet, or we did but locally - provide a temp file
if ( m_file.isEmpty() || !m_bTemp )
{
QTemporaryFile tempFile;
tempFile.setAutoRemove(false);
tempFile.open();
m_file = tempFile.fileName();
m_bTemp = true;
}
// otherwise, we already had a temp file
}
}
void _k_slotJobFinished( KJob * job )
{
Q_ASSERT( job == m_job );
m_job = 0;
if (job->error())
emit document->canceled( job->errorString() );
else {
if ( openFile() ) {
emit document->completed();
}
else {
emit document->canceled(QString());
}
}
}
void _k_slotStatJobFinished(KJob * job)
{
Q_ASSERT(job == m_statJob);
m_statJob = 0;
// this could maybe confuse some apps? So for now we'll just fallback to KIO::get
// and error again. Well, maybe this even helps with wrong stat results.
if (!job->error()) {
const KUrl localUrl = static_cast<KIO::StatJob*>(job)->url();
if (localUrl.isLocalFile()) {
m_file = localUrl.toLocalFile();
openLocalFile();
return;
}
}
openRemoteFile();
}
void _k_slotGotMimeType(KIO::Job *job, const QString &mime)
{
- kDebug(1000) << mime;
+ dbgFile << mime;
Q_ASSERT(job == m_job); Q_UNUSED(job);
// set the mimetype only if it was not already set (for example, by the host application)
if (mimeType.isEmpty()) {
mimeType = mime.toLocal8Bit();
m_bAutoDetectedMime = true;
}
}
void _k_slotUploadFinished( KJob * )
{
if (m_uploadJob->error())
{
QFile::remove(m_uploadJob->srcUrl().toLocalFile());
m_uploadJob = 0;
if (m_duringSaveAs) {
document->setUrl(m_originalURL);
m_file = m_originalFilePath;
}
}
else
{
KUrl dirUrl( m_url );
dirUrl.setPath( dirUrl.directory() );
::org::kde::KDirNotify::emitFilesAdded( dirUrl.url() );
m_uploadJob = 0;
document->setModified( false );
emit document->completed();
m_saveOk = true;
}
m_duringSaveAs = false;
m_originalURL = KUrl();
m_originalFilePath.clear();
if (m_waitForSave) {
m_eventLoop.quit();
}
}
};
KisDocument::KisDocument()
: d(new Private(this))
{
d->undoStack = new UndoStack(this);
d->undoStack->setParent(this);
d->isEmpty = true;
d->filterManager = new KisImportExportManager(this, d->progressUpdater);
connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
setAutoSave(defaultAutoSave());
setObjectName(newObjectName());
d->docInfo = new KoDocumentInfo(this);
d->pageLayout.width = 0;
d->pageLayout.height = 0;
d->pageLayout.topMargin = 0;
d->pageLayout.bottomMargin = 0;
d->pageLayout.leftMargin = 0;
d->pageLayout.rightMargin = 0;
KConfigGroup cfgGrp(KisFactory::componentData().config(), "Undo");
d->undoStack->setUndoLimit(cfgGrp.readEntry("UndoLimit", 1000));
connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int)));
// preload the krita resources
KisResourceServerProvider::instance();
init();
undoStack()->setUndoLimit(KisConfig().undoStackLimit());
setBackupFile(KisConfig().backupFile());
gridData().setShowGrid(false);
KisConfig cfg;
gridData().setGrid(cfg.getGridHSpacing(), cfg.getGridVSpacing());
}
KisDocument::~KisDocument()
{
/**
* Push a timebomb, which will try to release the memory after
* the document has been deleted
*/
KisPaintDevice::createMemoryReleaseObject()->deleteLater();
d->autoSaveTimer.disconnect(this);
d->autoSaveTimer.stop();
delete d->filterManager;
// Despite being QObject they needs to be deleted before the image
delete d->shapeController;
delete d->koShapeController;
if (d->image) {
d->image->notifyAboutToBeDeleted();
}
// The following line trigger the deletion of the image
d->image.clear();
delete d;
}
void KisDocument::init()
{
delete d->nserver;
d->nserver = 0;
d->nserver = new KisNameServer(1);
Q_CHECK_PTR(d->nserver);
d->shapeController = new KisShapeController(this, d->nserver);
d->koShapeController = new KoShapeController(0, d->shapeController);
d->kraSaver = 0;
d->kraLoader = 0;
}
bool KisDocument::reload()
{
// XXX: reimplement!
return false;
}
bool KisDocument::exportDocument(const KUrl & _url)
{
bool ret;
d->isExporting = true;
//
// Preserve a lot of state here because we need to restore it in order to
// be able to fake a File --> Export. Can't do this in saveFile() because,
// for a start, KParts has already set url and m_file and because we need
// to restore the modified flag etc. and don't want to put a load on anyone
// reimplementing saveFile() (Note: importDocument() and exportDocument()
// will remain non-virtual).
//
KUrl oldURL = url();
QString oldFile = localFilePath();
bool wasModified = isModified();
QByteArray oldMimeType = mimeType();
// save...
ret = saveAs(_url);
//
// This is sooooo hacky :(
// Hopefully we will restore enough state.
//
- kDebug(30003) << "Restoring KisDocument state to before export";
+ dbgUI << "Restoring KisDocument state to before export";
// always restore url & m_file because KParts has changed them
// (regardless of failure or success)
setUrl(oldURL);
setLocalFilePath(oldFile);
// on successful export we need to restore modified etc. too
// on failed export, mimetype/modified hasn't changed anyway
if (ret) {
setModified(wasModified);
d->mimeType = oldMimeType;
}
d->isExporting = false;
return ret;
}
bool KisDocument::saveFile()
{
- kDebug(30003) << "doc=" << url().url();
+ dbgUI << "doc=" << url().url();
// Save it to be able to restore it after a failed save
const bool wasModified = isModified();
// The output format is set by koMainWindow, and by openFile
QByteArray outputMimeType = d->outputMimeType;
if (outputMimeType.isEmpty())
outputMimeType = d->outputMimeType = nativeFormatMimeType();
QApplication::setOverrideCursor(Qt::WaitCursor);
if (backupFile()) {
if (url().isLocalFile())
KBackup::backupFile(url().toLocalFile(), d->backupPath);
else {
KIO::UDSEntry entry;
if (KIO::NetAccess::stat(url(),
entry,
KisPart::instance()->currentMainwindow())) { // this file exists => backup
emit statusBarMessage(i18n("Making backup..."));
KUrl backup;
if (d->backupPath.isEmpty())
backup = url();
else
backup = d->backupPath + '/' + url().fileName();
backup.setPath(backup.path() + QString::fromLatin1("~"));
KFileItem item(entry, url());
Q_ASSERT(item.name() == url().fileName());
KIO::FileCopyJob *job = KIO::file_copy(url(), backup, item.permissions(), KIO::Overwrite | KIO::HideProgressInfo);
job->exec();
}
}
}
emit statusBarMessage(i18n("Saving..."));
qApp->processEvents();
bool ret = false;
bool suppressErrorDialog = false;
if (!isNativeFormat(outputMimeType)) {
- kDebug(30003) << "Saving to format" << outputMimeType << "in" << localFilePath();
+ dbgUI << "Saving to format" << outputMimeType << "in" << localFilePath();
// Not native format : save using export filter
KisImportExportFilter::ConversionStatus status = d->filterManager->exportDocument(localFilePath(), outputMimeType);
ret = status == KisImportExportFilter::OK;
suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph);
} else {
// Native format => normal save
Q_ASSERT(!localFilePath().isEmpty());
ret = saveNativeFormat(localFilePath());
}
if (ret) {
d->undoStack->setClean();
removeAutoSaveFiles();
// Restart the autosave timer
// (we don't want to autosave again 2 seconds after a real save)
setAutoSave(d->autoSaveDelay);
}
QApplication::restoreOverrideCursor();
if (!ret) {
if (!suppressErrorDialog) {
if (errorMessage().isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath()));
} else if (errorMessage() != "USER_CANCELED") {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", localFilePath(), errorMessage()));
}
}
// couldn't save file so this new URL is invalid
// FIXME: we should restore the current document's true URL instead of
// setting it to nothing otherwise anything that depends on the URL
// being correct will not work (i.e. the document will be called
// "Untitled" which may not be true)
//
// Update: now the URL is restored in KisMainWindow but really, this
// should still be fixed in KisDocument/KParts (ditto for file).
// We still resetURL() here since we may or may not have been called
// by KisMainWindow - Clarence
resetURL();
// As we did not save, restore the "was modified" status
setModified(wasModified);
}
if (ret) {
d->mimeType = outputMimeType;
setConfirmNonNativeSave(isExporting(), false);
}
emit clearStatusBarMessage();
return ret;
}
QByteArray KisDocument::mimeType() const
{
return d->mimeType;
}
void KisDocument::setMimeType(const QByteArray & mimeType)
{
d->mimeType = mimeType;
}
void KisDocument::setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag)
{
d->outputMimeType = mimeType;
d->specialOutputFlag = specialOutputFlag;
}
QByteArray KisDocument::outputMimeType() const
{
return d->outputMimeType;
}
int KisDocument::specialOutputFlag() const
{
return d->specialOutputFlag;
}
bool KisDocument::confirmNonNativeSave(const bool exporting) const
{
// "exporting ? 1 : 0" is different from "exporting" because a bool is
// usually implemented like an "int", not "unsigned : 1"
return d->confirmNonNativeSave [ exporting ? 1 : 0 ];
}
void KisDocument::setConfirmNonNativeSave(const bool exporting, const bool on)
{
d->confirmNonNativeSave [ exporting ? 1 : 0] = on;
}
bool KisDocument::saveInBatchMode() const
{
return d->filterManager->getBatchMode();
}
void KisDocument::setSaveInBatchMode(const bool batchMode)
{
d->filterManager->setBatchMode(batchMode);
}
bool KisDocument::isImporting() const
{
return d->isImporting;
}
bool KisDocument::isExporting() const
{
return d->isExporting;
}
void KisDocument::setCheckAutoSaveFile(bool b)
{
d->shouldCheckAutoSaveFile = b;
}
void KisDocument::setAutoErrorHandlingEnabled(bool b)
{
d->autoErrorHandlingEnabled = b;
}
bool KisDocument::isAutoErrorHandlingEnabled() const
{
return d->autoErrorHandlingEnabled;
}
void KisDocument::slotAutoSave()
{
if (d->modified && d->modifiedAfterAutosave && !d->isLoading) {
// Give a warning when trying to autosave an encrypted file when no password is known (should not happen)
if (d->specialOutputFlag == SaveEncrypted && d->password.isNull()) {
// That advice should also fix this error from occurring again
emit statusBarMessage(i18n("The password of this encrypted document is not known. Autosave aborted! Please save your work manually."));
} else {
connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int)));
emit statusBarMessage(i18n("Autosaving..."));
d->autosaving = true;
bool ret = saveNativeFormat(autoSaveFile(localFilePath()));
setModified(true);
if (ret) {
d->modifiedAfterAutosave = false;
d->autoSaveTimer.stop(); // until the next change
}
d->autosaving = false;
emit clearStatusBarMessage();
disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int)));
if (!ret && !d->disregardAutosaveFailure) {
emit statusBarMessage(i18n("Error during autosave! Partition full?"));
}
}
}
}
void KisDocument::setReadWrite(bool readwrite)
{
d->readwrite = readwrite;
setAutoSave(d->autoSaveDelay);
foreach(KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) {
mainWindow->setReadWrite(readwrite);
}
}
void KisDocument::setAutoSave(int delay)
{
d->autoSaveDelay = delay;
if (isReadWrite() && d->autoSaveDelay > 0)
d->autoSaveTimer.start(d->autoSaveDelay * 1000);
else
d->autoSaveTimer.stop();
}
KoDocumentInfo *KisDocument::documentInfo() const
{
return d->docInfo;
}
bool KisDocument::isModified() const
{
return d->modified;
}
bool KisDocument::saveNativeFormat(const QString & file)
{
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
const int emergencyAutoSaveInterval = 10; // sec
if (!d->image->tryBarrierLock()) {
if (isAutosaving()) {
setDisregardAutosaveFailure(true);
if (realAutoSaveInterval) {
setAutoSave(emergencyAutoSaveInterval);
}
return false;
} else {
d->image->requestStrokeEnd();
QApplication::processEvents();
if (!d->image->tryBarrierLock()) {
return false;
}
}
}
setDisregardAutosaveFailure(false);
d->lastErrorMessage.clear();
- //kDebug(30003) <<"Saving to store";
+ //dbgUI <<"Saving to store";
KoStore::Backend backend = KoStore::Auto;
if (d->specialOutputFlag == SaveAsDirectoryStore) {
backend = KoStore::Directory;
- kDebug(30003) << "Saving as uncompressed XML, using directory store.";
+ dbgUI << "Saving as uncompressed XML, using directory store.";
}
else if (d->specialOutputFlag == SaveAsFlatXML) {
- kDebug(30003) << "Saving as a flat XML file.";
+ dbgUI << "Saving as a flat XML file.";
QFile f(file);
if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
bool success = saveToStream(&f);
f.close();
return success;
} else
return false;
}
- kDebug(30003) << "KisDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType();
+ dbgUI << "KisDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType();
// TODO: use std::auto_ptr or create store on stack [needs API fixing],
// to remove all the 'delete store' in all the branches
KoStore *store = KoStore::createStore(file, KoStore::Write, d->outputMimeType, backend);
if (d->specialOutputFlag == SaveEncrypted && !d->password.isNull()) {
store->setPassword(d->password);
}
if (store->bad()) {
d->lastErrorMessage = i18n("Could not create the file for saving"); // more details needed?
delete store;
d->image->unlock();
setAutoSave(realAutoSaveInterval);
return false;
}
d->image->unlock();
setAutoSave(realAutoSaveInterval);
return saveNativeFormatCalligra(store);
}
bool KisDocument::saveNativeFormatCalligra(KoStore *store)
{
- kDebug(30003) << "Saving root";
+ dbgUI << "Saving root";
if (store->open("root")) {
KoStoreDevice dev(store);
if (!saveToStream(&dev) || !store->close()) {
- kDebug(30003) << "saveToStream failed";
+ dbgUI << "saveToStream failed";
delete store;
return false;
}
} else {
d->lastErrorMessage = i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"));
delete store;
return false;
}
if (store->open("documentinfo.xml")) {
QDomDocument doc = KisDocument::createDomDocument("document-info"
/*DTD name*/, "document-info" /*tag name*/, "1.1");
doc = d->docInfo->save(doc);
KoStoreDevice dev(store);
QByteArray s = doc.toByteArray(); // this is already Utf8!
(void)dev.write(s.data(), s.size());
(void)store->close();
}
if (store->open("preview.png")) {
// ### TODO: missing error checking (The partition could be full!)
savePreview(store);
(void)store->close();
}
if (!completeSaving(store)) {
delete store;
return false;
}
- kDebug(30003) << "Saving done of url:" << url().url();
+ dbgUI << "Saving done of url:" << url().url();
if (!store->finalize()) {
delete store;
return false;
}
// Success
delete store;
return true;
}
bool KisDocument::saveToStream(QIODevice *dev)
{
QDomDocument doc = saveXML();
// Save to buffer
QByteArray s = doc.toByteArray(); // utf8 already
dev->open(QIODevice::WriteOnly);
int nwritten = dev->write(s.data(), s.size());
if (nwritten != (int)s.size())
- kWarning(30003) << "wrote " << nwritten << "- expected" << s.size();
+ warnUI << "wrote " << nwritten << "- expected" << s.size();
return nwritten == (int)s.size();
}
QString KisDocument::checkImageMimeTypes(const QString &mimeType, const KUrl &url) const
{
if (!url.isLocalFile()) return mimeType;
if (url.toLocalFile().endsWith(".kpp")) return "image/png";
QStringList imageMimeTypes;
imageMimeTypes << "image/jpeg"
<< "image/x-psd" << "image/photoshop" << "image/x-photoshop" << "image/x-vnd.adobe.photoshop" << "image/vnd.adobe.photoshop"
<< "image/x-portable-pixmap" << "image/x-portable-graymap" << "image/x-portable-bitmap"
<< "application/pdf"
<< "image/x-exr"
<< "image/x-xcf"
<< "image/x-eps"
<< "image/png"
<< "image/bmp" << "image/x-xpixmap" << "image/gif" << "image/x-xbitmap"
<< "image/tiff"
<< "image/jp2";
if (!imageMimeTypes.contains(mimeType)) return mimeType;
int accuracy = 0;
QFile f(url.toLocalFile());
if (!f.open(QIODevice::ReadOnly)) {
- qWarning() << "Could not open file to check the mimetype" << url;
+ warnKrita << "Could not open file to check the mimetype" << url;
}
QByteArray ba = f.read(qMin(f.size(), (qint64)512)); // should be enough for images
KMimeType::Ptr mime = KMimeType::findByContent(ba, &accuracy);
f.close();
if (!mime) {
return mimeType;
}
// Checking the content failed as well, so let's fall back on the extension again
if (mime->name() == "application/octet-stream") {
return mimeType;
}
return mime->name();
}
// Called for embedded documents
bool KisDocument::saveToStore(KoStore *_store, const QString & _path)
{
- kDebug(30003) << "Saving document to store" << _path;
+ dbgUI << "Saving document to store" << _path;
_store->pushDirectory();
// Use the path as the internal url
if (_path.startsWith(STORE_PROTOCOL))
setUrl(KUrl(_path));
else // ugly hack to pass a relative URI
setUrl(KUrl(INTERNAL_PREFIX + _path));
// In the current directory we're the king :-)
if (_store->open("root")) {
KoStoreDevice dev(_store);
if (!saveToStream(&dev)) {
_store->close();
return false;
}
if (!_store->close())
return false;
}
if (!completeSaving(_store))
return false;
// Now that we're done leave the directory again
_store->popDirectory();
- kDebug(30003) << "Saved document to store";
+ dbgUI << "Saved document to store";
return true;
}
bool KisDocument::savePreview(KoStore *store)
{
QPixmap pix = generatePreview(QSize(256, 256));
const QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly));
KoStoreDevice io(store);
if (!io.open(QIODevice::WriteOnly))
return false;
if (! preview.save(&io, "PNG")) // ### TODO What is -9 in quality terms?
return false;
io.close();
return true;
}
QPixmap KisDocument::generatePreview(const QSize& size)
{
if (d->image) {
QRect bounds = d->image->bounds();
QSize newSize = bounds.size();
newSize.scale(size, Qt::KeepAspectRatio);
QImage image;
if (bounds.width() < 10000 && bounds.height() < 10000) {
image = d->image->convertToQImage(d->image->bounds(), 0);
}
else {
image = d->image->convertToQImage(QRect(0, 0, newSize.width(), newSize.height()), newSize, 0);
}
image = image.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
return QPixmap::fromImage(image);
}
return QPixmap(size);
}
QString KisDocument::autoSaveFile(const QString & path) const
{
QString retval;
// Using the extension allows to avoid relying on the mime magic when opening
KMimeType::Ptr mime = KMimeType::mimeType(nativeFormatMimeType());
if (! mime) {
qFatal("It seems your installation is broken/incomplete because we failed to load the native mimetype \"%s\".", nativeFormatMimeType().constData());
}
const QString extension = mime->mainExtension();
if (path.isEmpty()) {
// Never saved?
#ifdef Q_OS_WIN
// On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921)
- retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::tempPath()).arg(KisFactory::componentName()).arg(qApp->applicationPid()).arg(objectName()).arg(extension);
+ retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::tempPath()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
#else
// On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file
- retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::homePath()).arg(KisFactory::componentName()).arg(qApp->applicationPid()).arg(objectName()).arg(extension);
+ retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::homePath()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
#endif
} else {
KUrl url = KUrl::fromPath(path);
Q_ASSERT(url.isLocalFile());
QString dir = url.directory(KUrl::AppendTrailingSlash);
QString filename = url.fileName();
retval = QString("%1.%2-autosave%3").arg(dir).arg(filename).arg(extension);
}
return retval;
}
void KisDocument::setDisregardAutosaveFailure(bool disregardFailure)
{
d->disregardAutosaveFailure = disregardFailure;
}
bool KisDocument::importDocument(const KUrl & _url)
{
bool ret;
- kDebug(30003) << "url=" << _url.url();
+ dbgUI << "url=" << _url.url();
d->isImporting = true;
// open...
ret = openUrl(_url);
// reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a
// File --> Import
if (ret) {
- kDebug(30003) << "success, resetting url";
+ dbgUI << "success, resetting url";
resetURL();
setTitleModified();
}
d->isImporting = false;
return ret;
}
bool KisDocument::openUrl(const KUrl & _url, KisDocument::OpenUrlFlags flags)
{
- kDebug(30003) << "url=" << _url.url();
+ dbgUI << "url=" << _url.url();
d->lastErrorMessage.clear();
// Reimplemented, to add a check for autosave files and to improve error reporting
if (!_url.isValid()) {
d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ?
return false;
}
KUrl url(_url);
bool autosaveOpened = false;
d->isLoading = true;
if (url.isLocalFile() && d->shouldCheckAutoSaveFile) {
QString file = url.toLocalFile();
QString asf = autoSaveFile(file);
if (QFile::exists(asf)) {
- //kDebug(30003) <<"asf=" << asf;
+ //dbgUI <<"asf=" << asf;
// ## TODO compare timestamps ?
int res = QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("An autosaved file exists for this document.\nDo you want to open it instead?"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
switch (res) {
case QMessageBox::Yes :
url.setPath(asf);
autosaveOpened = true;
break;
case QMessageBox::No :
QFile::remove(asf);
break;
default: // Cancel
d->isLoading = false;
return false;
}
}
}
bool ret = openUrlInternal(url);
if (autosaveOpened) {
resetURL(); // Force save to act like 'Save As'
setReadWrite(true); // enable save button
setModified(true);
}
else {
if( !(flags & OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES) ) {
KisPart::instance()->addRecentURLToAllMainWindows(_url);
}
if (ret) {
// Detect readonly local-files; remote files are assumed to be writable, unless we add a KIO::stat here (async).
KFileItem file(url, mimeType(), KFileItem::Unknown);
setReadWrite(file.isWritable());
}
}
return ret;
}
bool KisDocument::openFile()
{
- //kDebug(30003) <<"for" << localFilePath();
+ //dbgUI <<"for" << localFilePath();
if (!QFile::exists(localFilePath())) {
QApplication::restoreOverrideCursor();
if (d->autoErrorHandlingEnabled)
// Maybe offer to create a new document with that name ?
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath()));
d->isLoading = false;
return false;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
d->specialOutputFlag = 0;
QByteArray _native_format = nativeFormatMimeType();
KUrl u(localFilePath());
QString typeName = mimeType();
if (typeName.isEmpty()) {
typeName = KMimeType::findByUrl(u, 0, true)->name();
}
// for images, always check content.
typeName = checkImageMimeTypes(typeName, u);
- //kDebug(30003) << "mimetypes 4:" << typeName;
+ //dbgUI << "mimetypes 4:" << typeName;
// Allow to open backup files, don't keep the mimetype application/x-trash.
if (typeName == "application/x-trash") {
QString path = u.path();
KMimeType::Ptr mime = KMimeType::mimeType(typeName);
const QStringList patterns = mime ? mime->patterns() : QStringList();
// Find the extension that makes it a backup file, and remove it
for (QStringList::ConstIterator it = patterns.begin(); it != patterns.end(); ++it) {
QString ext = *it;
if (!ext.isEmpty() && ext[0] == '*') {
ext.remove(0, 1);
if (path.endsWith(ext)) {
path.chop(ext.length());
break;
}
}
}
typeName = KMimeType::findByPath(path, 0, true)->name();
}
// Special case for flat XML files (e.g. using directory store)
if (u.fileName() == "maindoc.xml" || u.fileName() == "content.xml" || typeName == "inode/directory") {
typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype
d->specialOutputFlag = SaveAsDirectoryStore;
- kDebug(30003) << "loading" << u.fileName() << ", using directory store for" << localFilePath() << "; typeName=" << typeName;
+ dbgUI << "loading" << u.fileName() << ", using directory store for" << localFilePath() << "; typeName=" << typeName;
}
- kDebug(30003) << localFilePath() << "type:" << typeName;
+ dbgUI << localFilePath() << "type:" << typeName;
QString importedFile = localFilePath();
// create the main progress monitoring object for loading, this can
// contain subtasks for filtering and loading
KoProgressProxy *progressProxy = 0;
if (d->progressProxy) {
progressProxy = d->progressProxy;
}
d->progressUpdater = new KoProgressUpdater(progressProxy,
KoProgressUpdater::Unthreaded,
d->profileStream);
d->progressUpdater->setReferenceTime(d->profileReferenceTime);
d->progressUpdater->start(100, i18n("Opening Document"));
setupOpenFileSubProgress();
if (!isNativeFormat(typeName.toLatin1())) {
KisImportExportFilter::ConversionStatus status;
importedFile = d->filterManager->importDocument(localFilePath(), typeName, status);
if (status != KisImportExportFilter::OK) {
QApplication::restoreOverrideCursor();
QString msg;
switch (status) {
case KisImportExportFilter::OK: break;
case KisImportExportFilter::FilterCreationError:
msg = i18n("Could not create the filter plugin"); break;
case KisImportExportFilter::CreationError:
msg = i18n("Could not create the output document"); break;
case KisImportExportFilter::FileNotFound:
msg = i18n("File not found"); break;
case KisImportExportFilter::StorageCreationError:
msg = i18n("Cannot create storage"); break;
case KisImportExportFilter::BadMimeType:
msg = i18n("Bad MIME type"); break;
case KisImportExportFilter::EmbeddedDocError:
msg = i18n("Error in embedded document"); break;
case KisImportExportFilter::WrongFormat:
msg = i18n("Format not recognized"); break;
case KisImportExportFilter::NotImplemented:
msg = i18n("Not implemented"); break;
case KisImportExportFilter::ParsingError:
msg = i18n("Parsing error"); break;
case KisImportExportFilter::PasswordProtected:
msg = i18n("Document is password protected"); break;
case KisImportExportFilter::InvalidFormat:
msg = i18n("Invalid file format"); break;
case KisImportExportFilter::InternalError:
case KisImportExportFilter::UnexpectedEOF:
case KisImportExportFilter::UnexpectedOpcode:
case KisImportExportFilter::StupidError: // ?? what is this ??
case KisImportExportFilter::UsageError:
msg = i18n("Internal error"); break;
case KisImportExportFilter::OutOfMemory:
msg = i18n("Out of memory"); break;
case KisImportExportFilter::FilterEntryNull:
msg = i18n("Empty Filter Plugin"); break;
case KisImportExportFilter::NoDocumentCreated:
msg = i18n("Trying to load into the wrong kind of document"); break;
case KisImportExportFilter::DownloadFailed:
msg = i18n("Failed to download remote file"); break;
case KisImportExportFilter::UserCancelled:
case KisImportExportFilter::BadConversionGraph:
// intentionally we do not prompt the error message here
break;
default: msg = i18n("Unknown error"); break;
}
if (d->autoErrorHandlingEnabled && !msg.isEmpty()) {
QString errorMsg(i18n("Could not open %2.\nReason: %1.\n%3", msg, prettyPathOrUrl(), errorMessage()));
QMessageBox::critical(0, i18nc("@title:window", "Krita"), errorMsg);
}
d->isLoading = false;
delete d->progressUpdater;
d->progressUpdater = 0;
return false;
}
d->isEmpty = false;
- kDebug(30003) << "importedFile" << importedFile << "status:" << static_cast<int>(status);
+ dbgUI << "importedFile" << importedFile << "status:" << static_cast<int>(status);
}
QApplication::restoreOverrideCursor();
bool ok = true;
if (!importedFile.isEmpty()) { // Something to load (tmp or native file) ?
// The filter, if any, has been applied. It's all native format now.
if (!loadNativeFormat(importedFile)) {
ok = false;
if (d->autoErrorHandlingEnabled) {
showLoadingErrorDialog();
}
}
}
if (importedFile != localFilePath()) {
// We opened a temporary file (result of an import filter)
// Set document URL to empty - we don't want to save in /tmp !
// But only if in readwrite mode (no saving problem otherwise)
// --
// But this isn't true at all. If this is the result of an
// import, then importedFile=temporary_file.kwd and
// file/m_url=foreignformat.ext so m_url is correct!
// So don't resetURL() or else the caption won't be set when
// foreign files are opened (an annoying bug).
// - Clarence
//
#if 0
if (isReadWrite())
resetURL();
#endif
// remove temp file - uncomment this to debug import filters
if (!importedFile.isEmpty()) {
#ifndef NDEBUG
if (!getenv("CALLIGRA_DEBUG_FILTERS"))
#endif
QFile::remove(importedFile);
}
}
if (ok) {
setMimeTypeAfterLoading(typeName);
emit sigLoadingFinished();
}
if (progressUpdater()) {
QPointer<KoUpdater> updater
= progressUpdater()->startSubtask(1, "clear undo stack");
updater->setProgress(0);
undoStack()->clear();
updater->setProgress(100);
}
delete d->progressUpdater;
d->progressUpdater = 0;
d->isLoading = false;
return ok;
}
KoProgressUpdater *KisDocument::progressUpdater() const
{
return d->progressUpdater;
}
void KisDocument::setProgressProxy(KoProgressProxy *progressProxy)
{
d->progressProxy = progressProxy;
}
KoProgressProxy* KisDocument::progressProxy() const
{
if (!d->progressProxy) {
KisMainWindow *mainWindow = 0;
if (KisPart::instance()->mainwindowCount() > 0) {
mainWindow = KisPart::instance()->mainWindows()[0];
}
d->progressProxy = new DocumentProgressProxy(mainWindow);
}
return d->progressProxy;
}
// shared between openFile and koMainWindow's "create new empty document" code
void KisDocument::setMimeTypeAfterLoading(const QString& mimeType)
{
d->mimeType = mimeType.toLatin1();
d->outputMimeType = d->mimeType;
const bool needConfirm = !isNativeFormat(d->mimeType);
setConfirmNonNativeSave(false, needConfirm);
setConfirmNonNativeSave(true, needConfirm);
}
// The caller must call store->close() if loadAndParse returns true.
bool KisDocument::oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc)
{
- //kDebug(30003) <<"Trying to open" << filename;
+ //dbgUI <<"Trying to open" << filename;
if (!store->open(filename)) {
- kWarning(30003) << "Entry " << filename << " not found!";
+ warnUI << "Entry " << filename << " not found!";
d->lastErrorMessage = i18n("Could not find %1", filename);
return false;
}
// Error variables for QDomDocument::setContent
QString errorMsg;
int errorLine, errorColumn;
bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn);
store->close();
if (!ok) {
- kError(30003) << "Parsing error in " << filename << "! Aborting!" << endl
+ errUI << "Parsing error in " << filename << "! Aborting!" << endl
<< " In line: " << errorLine << ", column: " << errorColumn << endl
<< " Error message: " << errorMsg << endl;
d->lastErrorMessage = i18n("Parsing error in %1 at line %2, column %3\nError message: %4"
, filename , errorLine, errorColumn ,
QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0,
QCoreApplication::UnicodeUTF8));
return false;
}
- kDebug(30003) << "File" << filename << " loaded and parsed";
+ dbgUI << "File" << filename << " loaded and parsed";
return true;
}
bool KisDocument::loadNativeFormat(const QString & file_)
{
QString file = file_;
QFileInfo fileInfo(file);
if (!fileInfo.exists()) { // check duplicated from openUrl, but this is useful for templates
d->lastErrorMessage = i18n("The file %1 does not exist.", file);
return false;
}
if (!fileInfo.isFile()) {
file += "/content.xml";
QFileInfo fileInfo2(file);
if (!fileInfo2.exists() || !fileInfo2.isFile()) {
d->lastErrorMessage = i18n("%1 is not a file." , file_);
return false;
}
}
QApplication::setOverrideCursor(Qt::WaitCursor);
- kDebug(30003) << file;
+ dbgUI << file;
QFile in;
bool isRawXML = false;
if (d->specialOutputFlag != SaveAsDirectoryStore) { // Don't try to open a directory ;)
in.setFileName(file);
if (!in.open(QIODevice::ReadOnly)) {
QApplication::restoreOverrideCursor();
d->lastErrorMessage = i18n("Could not open the file for reading (check read permissions).");
return false;
}
char buf[6];
buf[5] = 0;
int pos = 0;
do {
if (in.read(buf + pos , 1) < 1) {
QApplication::restoreOverrideCursor();
in.close();
d->lastErrorMessage = i18n("Could not read the beginning of the file.");
return false;
}
if (QChar(buf[pos]).isSpace())
continue;
pos++;
} while (pos < 5);
isRawXML = (qstrnicmp(buf, "<?xml", 5) == 0);
if (! isRawXML)
// also check for broken MathML files, which seem to be rather common
isRawXML = (qstrnicmp(buf, "<math", 5) == 0); // file begins with <math ?
- //kDebug(30003) <<"PATTERN=" << buf;
+ //dbgUI <<"PATTERN=" << buf;
}
// Is it plain XML?
if (isRawXML) {
in.seek(0);
QString errorMsg;
int errorLine;
int errorColumn;
KoXmlDocument doc = KoXmlDocument(true);
bool res;
if (doc.setContent(&in, &errorMsg, &errorLine, &errorColumn)) {
res = loadXML(doc, 0);
if (res)
res = completeLoading(0);
} else {
- kError(30003) << "Parsing Error! Aborting! (in KisDocument::loadNativeFormat (QFile))" << endl
+ errUI << "Parsing Error! Aborting! (in KisDocument::loadNativeFormat (QFile))" << endl
<< " Line: " << errorLine << " Column: " << errorColumn << endl
<< " Message: " << errorMsg << endl;
d->lastErrorMessage = i18n("parsing error in the main document at line %1, column %2\nError message: %3", errorLine, errorColumn, i18n(errorMsg.toUtf8()));
res = false;
}
QApplication::restoreOverrideCursor();
in.close();
d->isEmpty = false;
return res;
}
else { // It's a calligra store (tar.gz, zip, directory, etc.)
in.close();
KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
KoStore *store = KoStore::createStore(file, KoStore::Read, "", backend);
if (store->bad()) {
d->lastErrorMessage = i18n("Not a valid Krita file: %1", file);
delete store;
QApplication::restoreOverrideCursor();
return false;
}
// Remember that the file was encrypted
if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting)
d->specialOutputFlag = SaveEncrypted;
const bool success = loadNativeFormatFromStoreInternal(store);
// Retrieve the password after loading the file, only then is it guaranteed to exist
if (success && store->isEncrypted() && !d->isImporting)
d->password = store->password();
delete store;
return success;
}
}
bool KisDocument::loadNativeFormatFromByteArray(QByteArray &data)
{
bool succes;
KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto;
QBuffer buffer(&data);
KoStore *store = KoStore::createStore(&buffer, KoStore::Read, "", backend);
if (store->bad()) {
delete store;
return false;
}
// Remember that the file was encrypted
if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting)
d->specialOutputFlag = SaveEncrypted;
succes = loadNativeFormatFromStoreInternal(store);
// Retrieve the password after loading the file, only then is it guaranteed to exist
if (succes && store->isEncrypted() && !d->isImporting)
d->password = store->password();
delete store;
return succes;
}
bool KisDocument::loadNativeFormatFromStoreInternal(KoStore *store)
{
if (store->hasFile("root") || store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml)
KoXmlDocument doc = KoXmlDocument(true);
bool ok = oldLoadAndParse(store, "root", doc);
if (ok)
ok = loadXML(doc, store);
if (!ok) {
QApplication::restoreOverrideCursor();
return false;
}
} else {
- kError(30003) << "ERROR: No maindoc.xml" << endl;
+ errUI << "ERROR: No maindoc.xml" << endl;
d->lastErrorMessage = i18n("Invalid document: no file 'maindoc.xml'.");
QApplication::restoreOverrideCursor();
return false;
}
if (store->hasFile("documentinfo.xml")) {
KoXmlDocument doc = KoXmlDocument(true);
if (oldLoadAndParse(store, "documentinfo.xml", doc)) {
d->docInfo->load(doc);
}
} else {
- //kDebug( 30003 ) <<"cannot open document info";
+ //dbgUI <<"cannot open document info";
delete d->docInfo;
d->docInfo = new KoDocumentInfo(this);
}
bool res = completeLoading(store);
QApplication::restoreOverrideCursor();
d->isEmpty = false;
return res;
}
// For embedded documents
bool KisDocument::loadFromStore(KoStore *_store, const QString& url)
{
if (_store->open(url)) {
KoXmlDocument doc = KoXmlDocument(true);
doc.setContent(_store->device());
if (!loadXML(doc, _store)) {
_store->close();
return false;
}
_store->close();
} else {
- kWarning() << "couldn't open " << url;
+ dbgKrita << "couldn't open " << url;
}
_store->pushDirectory();
// Store as document URL
if (url.startsWith(STORE_PROTOCOL)) {
setUrl(url);
} else {
setUrl(KUrl(INTERNAL_PREFIX + url));
_store->enterDirectory(url);
}
bool result = completeLoading(_store);
// Restore the "old" path
_store->popDirectory();
return result;
}
bool KisDocument::loadOdf(KoOdfReadStore & odfStore)
{
Q_UNUSED(odfStore);
setErrorMessage(i18n("Krita does not support the OpenDocument file format."));
return false;
}
bool KisDocument::saveOdf(SavingContext &documentContext)
{
Q_UNUSED(documentContext);
setErrorMessage(i18n("Krita does not support the OpenDocument file format."));
return false;
}
bool KisDocument::isStoredExtern() const
{
return !storeInternal() && hasExternURL();
}
void KisDocument::setModified()
{
d->modified = true;
}
void KisDocument::setModified(bool mod)
{
if (isAutosaving()) // ignore setModified calls due to autosaving
return;
if ( !d->readwrite && d->modified ) {
kError(1000) << "Can't set a read-only document to 'modified' !" << endl;
return;
}
- //kDebug(30003)<<" url:" << url.path();
- //kDebug(30003)<<" mod="<<mod<<" MParts mod="<<KisParts::ReadWritePart::isModified()<<" isModified="<<isModified();
+ //dbgUI<<" url:" << url.path();
+ //dbgUI<<" mod="<<mod<<" MParts mod="<<KisParts::ReadWritePart::isModified()<<" isModified="<<isModified();
if (mod && !d->modifiedAfterAutosave) {
// First change since last autosave -> start the autosave timer
setAutoSave(d->autoSaveDelay);
}
d->modifiedAfterAutosave = mod;
if (mod == isModified())
return;
d->modified = mod;
if (mod) {
d->isEmpty = false;
documentInfo()->updateParameters();
}
// This influences the title
setTitleModified();
emit modified(mod);
}
QString KisDocument::prettyPathOrUrl() const
{
QString _url( url().pathOrUrl() );
#ifdef Q_OS_WIN
if (url().isLocalFile()) {
_url = QDir::toNativeSeparators(_url);
}
#endif
return _url;
}
// Get caption from document info (title(), in about page)
QString KisDocument::caption() const
{
QString c;
if (documentInfo()) {
c = documentInfo()->aboutInfo("title");
}
const QString _url(url().fileName());
if (!c.isEmpty() && !_url.isEmpty()) {
c = QString("%1 - %2").arg(c).arg(_url);
}
else if (c.isEmpty()) {
c = _url; // Fall back to document URL
}
return c;
}
void KisDocument::setTitleModified()
{
emit titleModified(caption(), isModified());
}
bool KisDocument::completeLoading(KoStore* store)
{
if (!d->image) {
if (d->kraLoader->errorMessages().isEmpty()) {
setErrorMessage(i18n("Unknown error."));
}
else {
setErrorMessage(d->kraLoader->errorMessages().join(".\n"));
}
return false;
}
d->kraLoader->loadBinaryData(store, d->image, url().url(), isStoredExtern());
bool retval = true;
if (!d->kraLoader->errorMessages().isEmpty()) {
setErrorMessage(d->kraLoader->errorMessages().join(".\n"));
retval = false;
}
if (retval) {
vKisNodeSP preselectedNodes = d->kraLoader->selectedNodes();
if (preselectedNodes.size() > 0) {
d->preActivatedNode = preselectedNodes.first();
}
// before deleting the kraloader, get the list with preloaded assistants and save it
d->assistants = d->kraLoader->assistants();
d->shapeController->setImage(d->image);
connect(d->image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified()));
if (d->image) {
d->image->initialRefreshGraph();
}
setAutoSave(KisConfig().autoSaveInterval());
emit sigLoadingFinished();
}
delete d->kraLoader;
d->kraLoader = 0;
return retval;
}
bool KisDocument::completeSaving(KoStore* store)
{
QString uri = url().url();
d->kraSaver->saveBinaryData(store, d->image, url().url(), isStoredExtern(), isAutosaving());
bool retval = true;
if (!d->kraSaver->errorMessages().isEmpty()) {
setErrorMessage(d->kraSaver->errorMessages().join(".\n"));
retval = false;
}
delete d->kraSaver;
d->kraSaver = 0;
emit sigSavingFinished();
return retval;
}
QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const
{
- return createDomDocument(KisFactory::componentName(), tagName, version);
+ return createDomDocument("krita", tagName, version);
}
//static
QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version)
{
QDomImplementation impl;
QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version);
QDomDocumentType dtype = impl.createDocumentType(tagName,
QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version),
url);
// The namespace URN doesn't need to include the version number.
QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName);
QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype);
doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement());
return doc;
}
bool KisDocument::loadXML(const KoXmlDocument& doc, KoStore *store)
{
Q_UNUSED(store);
if (d->image) {
d->shapeController->setImage(0);
d->image = 0;
}
KoXmlElement root;
KoXmlNode node;
KisImageWSP image;
init();
if (doc.doctype().name() != "DOC") {
setErrorMessage(i18n("The format is not supported or the file is corrupted"));
return false;
}
root = doc.documentElement();
int syntaxVersion = root.attribute("syntaxVersion", "3").toInt();
if (syntaxVersion > 2) {
setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion));
return false;
}
if (!root.hasChildNodes()) {
setErrorMessage(i18n("The file has no layers."));
return false;
}
if (d->kraLoader) delete d->kraLoader;
d->kraLoader = new KisKraLoader(this, syntaxVersion);
// Legacy from the multi-image .kra file period.
for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) {
if (node.isElement()) {
if (node.nodeName() == "IMAGE") {
KoXmlElement elem = node.toElement();
if (!(image = d->kraLoader->loadXML(elem))) {
if (d->kraLoader->errorMessages().isEmpty()) {
setErrorMessage(i18n("Unknown error."));
}
else {
setErrorMessage(d->kraLoader->errorMessages().join(".\n"));
}
return false;
}
}
else {
if (d->kraLoader->errorMessages().isEmpty()) {
setErrorMessage(i18n("The file does not contain an image."));
}
return false;
}
}
}
if (d->image) {
// Disconnect existing sig/slot connections
d->image->disconnect(this);
}
d->image = image;
return true;
}
QDomDocument KisDocument::saveXML()
{
dbgFile << url();
QDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION);
QDomElement root = doc.documentElement();
root.setAttribute("editor", "Krita");
root.setAttribute("syntaxVersion", "2");
if (d->kraSaver) delete d->kraSaver;
d->kraSaver = new KisKraSaver(this);
root.appendChild(d->kraSaver->saveXML(doc, d->image));
if (!d->kraSaver->errorMessages().isEmpty()) {
setErrorMessage(d->kraSaver->errorMessages().join(".\n"));
}
return doc;
}
bool KisDocument::isNativeFormat(const QByteArray& mimetype) const
{
if (mimetype == nativeFormatMimeType())
return true;
return extraNativeMimeTypes().contains(mimetype);
}
int KisDocument::supportedSpecialFormats() const
{
return 0; // we don't support encryption.
}
void KisDocument::setErrorMessage(const QString& errMsg)
{
d->lastErrorMessage = errMsg;
}
QString KisDocument::errorMessage() const
{
return d->lastErrorMessage;
}
void KisDocument::showLoadingErrorDialog()
{
if (errorMessage().isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open\n%1", localFilePath()));
}
else if (errorMessage() != "USER_CANCELED") {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open %1\nReason: %2", localFilePath(), errorMessage()));
}
}
bool KisDocument::isAutosaving() const
{
return d->autosaving;
}
bool KisDocument::isLoading() const
{
return d->isLoading;
}
void KisDocument::removeAutoSaveFiles()
{
// Eliminate any auto-save file
QString asf = autoSaveFile(localFilePath()); // the one in the current dir
if (QFile::exists(asf))
QFile::remove(asf);
asf = autoSaveFile(QString()); // and the one in $HOME
if (QFile::exists(asf))
QFile::remove(asf);
}
void KisDocument::setBackupFile(bool _b)
{
d->backupFile = _b;
}
bool KisDocument::backupFile()const
{
return d->backupFile;
}
void KisDocument::setBackupPath(const QString & _path)
{
d->backupPath = _path;
}
QString KisDocument::backupPath()const
{
return d->backupPath;
}
bool KisDocument::storeInternal() const
{
return d->storeInternal;
}
void KisDocument::setStoreInternal(bool i)
{
d->storeInternal = i;
- //kDebug(30003)<<"="<<d->storeInternal<<" doc:"<<url().url();
+ //dbgUI<<"="<<d->storeInternal<<" doc:"<<url().url();
}
bool KisDocument::hasExternURL() const
{
return !url().protocol().isEmpty()
&& url().protocol() != STORE_PROTOCOL
&& url().protocol() != INTERNAL_PROTOCOL;
}
static const struct {
const char *localName;
const char *documentType;
} TN2DTArray[] = {
{ "text", I18N_NOOP("a word processing") },
{ "spreadsheet", I18N_NOOP("a spreadsheet") },
{ "presentation", I18N_NOOP("a presentation") },
{ "chart", I18N_NOOP("a chart") },
{ "drawing", I18N_NOOP("a drawing") }
};
static const unsigned int numTN2DT = sizeof(TN2DTArray) / sizeof(*TN2DTArray);
QString KisDocument::tagNameToDocumentType(const QString& localName)
{
for (unsigned int i = 0 ; i < numTN2DT ; ++i)
if (localName == TN2DTArray[i].localName)
return i18n(TN2DTArray[i].documentType);
return localName;
}
KoPageLayout KisDocument::pageLayout(int /*pageNumber*/) const
{
return d->pageLayout;
}
void KisDocument::setPageLayout(const KoPageLayout &pageLayout)
{
d->pageLayout = pageLayout;
}
KoUnit KisDocument::unit() const
{
return d->unit;
}
void KisDocument::setUnit(const KoUnit &unit)
{
if (d->unit != unit) {
d->unit = unit;
emit unitChanged(unit);
}
}
void KisDocument::saveUnitOdf(KoXmlWriter *settingsWriter) const
{
settingsWriter->addConfigItem("unit", unit().symbol());
}
KUndo2Stack *KisDocument::undoStack()
{
return d->undoStack;
}
void KisDocument::addCommand(KUndo2Command *command)
{
if (command)
d->undoStack->push(command);
}
void KisDocument::beginMacro(const KUndo2MagicString & text)
{
d->undoStack->beginMacro(text);
}
void KisDocument::endMacro()
{
d->undoStack->endMacro();
}
void KisDocument::slotUndoStackIndexChanged(int idx)
{
// even if the document was already modified, call setModified to re-start autosave timer
setModified(idx != d->undoStack->cleanIndex());
}
void KisDocument::setProfileStream(QTextStream *profilestream)
{
d->profileStream = profilestream;
}
void KisDocument::setProfileReferenceTime(const QTime& referenceTime)
{
d->profileReferenceTime = referenceTime;
}
void KisDocument::clearUndoHistory()
{
d->undoStack->clear();
}
KoGridData &KisDocument::gridData()
{
return d->gridData;
}
KoGuidesData &KisDocument::guidesData()
{
return d->guidesData;
}
bool KisDocument::isEmpty() const
{
return d->isEmpty;
}
void KisDocument::setEmpty()
{
d->isEmpty = true;
}
// static
int KisDocument::defaultAutoSave()
{
return 300;
}
void KisDocument::resetURL() {
setUrl(KUrl());
setLocalFilePath(QString());
}
int KisDocument::pageCount() const {
return 1;
}
void KisDocument::setupOpenFileSubProgress() {}
KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const
{
return new KoDocumentInfoDlg(parent, docInfo);
}
bool KisDocument::isReadWrite() const
{
return d->readwrite;
}
KUrl KisDocument::url() const
{
return d->m_url;
}
bool KisDocument::closeUrl(bool promptToSave)
{
if (promptToSave) {
if ( d->document->isReadWrite() && d->document->isModified()) {
foreach(KisView *view, KisPart::instance()->views()) {
if (view && view->document() == this) {
if (!view->queryClose()) {
return false;
}
}
}
}
}
// Not modified => ok and delete temp file.
d->mimeType = QByteArray();
if ( d->m_bTemp )
{
QFile::remove( d->m_file );
d->m_bTemp = false;
}
// It always succeeds for a read-only part,
// but the return value exists for reimplementations
// (e.g. pressing cancel for a modified read-write part)
return true;
}
bool KisDocument::saveAs( const KUrl & kurl )
{
if (!kurl.isValid())
{
kError(1000) << "saveAs: Malformed URL " << kurl.url() << endl;
return false;
}
d->m_duringSaveAs = true;
d->m_originalURL = d->m_url;
d->m_originalFilePath = d->m_file;
d->m_url = kurl; // Store where to upload in saveToURL
d->prepareSaving();
bool result = save(); // Save local file and upload local file
if (!result) {
d->m_url = d->m_originalURL;
d->m_file = d->m_originalFilePath;
d->m_duringSaveAs = false;
d->m_originalURL = KUrl();
d->m_originalFilePath.clear();
}
return result;
}
bool KisDocument::save()
{
d->m_saveOk = false;
if ( d->m_file.isEmpty() ) // document was created empty
d->prepareSaving();
DocumentProgressProxy *progressProxy = 0;
if (!d->document->progressProxy()) {
KisMainWindow *mainWindow = 0;
if (KisPart::instance()->mainwindowCount() > 0) {
mainWindow = KisPart::instance()->mainWindows()[0];
}
progressProxy = new DocumentProgressProxy(mainWindow);
d->document->setProgressProxy(progressProxy);
}
d->document->setUrl(url());
// THIS IS WRONG! KisDocument::saveFile should move here, and whoever subclassed KisDocument to
// reimplement saveFile shold now subclass KisPart.
bool ok = d->document->saveFile();
if (progressProxy) {
d->document->setProgressProxy(0);
delete progressProxy;
}
if (ok) {
return saveToUrl();
}
else {
emit canceled(QString());
}
return false;
}
bool KisDocument::waitSaveComplete()
{
if (!d->m_uploadJob)
return d->m_saveOk;
d->m_waitForSave = true;
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
d->m_waitForSave = false;
return d->m_saveOk;
}
void KisDocument::setUrl(const KUrl &url)
{
d->m_url = url;
}
QString KisDocument::localFilePath() const
{
return d->m_file;
}
void KisDocument::setLocalFilePath( const QString &localFilePath )
{
d->m_file = localFilePath;
}
bool KisDocument::saveToUrl()
{
if ( d->m_url.isLocalFile() ) {
d->document->setModified( false );
emit completed();
// if m_url is a local file there won't be a temp file -> nothing to remove
Q_ASSERT( !d->m_bTemp );
d->m_saveOk = true;
d->m_duringSaveAs = false;
d->m_originalURL = KUrl();
d->m_originalFilePath.clear();
return true; // Nothing to do
}
#ifndef Q_OS_WIN
else {
if (d->m_uploadJob) {
QFile::remove(d->m_uploadJob->srcUrl().toLocalFile());
d->m_uploadJob->kill();
d->m_uploadJob = 0;
}
QTemporaryFile *tempFile = new QTemporaryFile();
tempFile->open();
QString uploadFile = tempFile->fileName();
delete tempFile;
KUrl uploadUrl;
uploadUrl.setPath( uploadFile );
// Create hardlink
if (::link(QFile::encodeName(d->m_file), QFile::encodeName(uploadFile)) != 0) {
// Uh oh, some error happened.
return false;
}
d->m_uploadJob = KIO::file_move( uploadUrl, d->m_url, -1, KIO::Overwrite );
connect( d->m_uploadJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotUploadFinished(KJob*)) );
return true;
}
#else
return false;
#endif
}
bool KisDocument::openUrlInternal(const KUrl &url)
{
if ( !url.isValid() )
return false;
if (d->m_bAutoDetectedMime) {
d->mimeType = QByteArray();
d->m_bAutoDetectedMime = false;
}
QByteArray mimetype = d->mimeType;
if ( !closeUrl() )
return false;
d->mimeType = mimetype;
setUrl(url);
d->m_file.clear();
if (d->m_url.isLocalFile()) {
d->m_file = d->m_url.toLocalFile();
return d->openLocalFile();
}
else {
d->openRemoteFile();
return true;
}
}
KisImageWSP KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* colorspace)
{
KoColor backgroundColor(Qt::white, colorspace);
/**
* FIXME: check whether this is a good value
*/
double defaultResolution=1.;
newImage(name, width, height, colorspace, backgroundColor, "",
defaultResolution);
return image();
}
bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution) {
return newImage(name, width, height, cs, bgColor, false, 1, imageDescription, imageResolution);
}
bool KisDocument::newImage(const QString& name,
qint32 width, qint32 height,
const KoColorSpace* cs,
const KoColor &bgColor, bool backgroundAsLayer,
int numberOfLayers,
const QString &description, const double imageResolution)
{
Q_ASSERT(cs);
init();
KisConfig cfg;
KisImageWSP image;
KisPaintLayerSP layer;
if (!cs) return false;
QApplication::setOverrideCursor(Qt::BusyCursor);
image = new KisImage(createUndoStore(), width, height, cs, name);
Q_CHECK_PTR(image);
connect(image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified()));
image->setResolution(imageResolution, imageResolution);
image->assignImageProfile(cs->profile());
documentInfo()->setAboutInfo("title", name);
if (name != i18n("Unnamed") && !name.isEmpty()) {
setUrl(KUrl(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra"));
}
documentInfo()->setAboutInfo("comments", description);
layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
Q_CHECK_PTR(layer);
if (backgroundAsLayer) {
image->setDefaultProjectionColor(KoColor(cs));
if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) {
layer->paintDevice()->setDefaultPixel(bgColor.data());
} else {
// Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel
KisFillPainter painter;
painter.begin(layer->paintDevice());
painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8());
}
} else {
image->setDefaultProjectionColor(bgColor);
}
layer->setDirty(QRect(0, 0, width, height));
image->addNode(layer.data(), image->rootLayer().data());
setCurrentImage(image);
for(int i = 1; i < numberOfLayers; ++i) {
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
image->addNode(layer, image->root(), i);
layer->setDirty(QRect(0, 0, width, height));
}
cfg.defImageWidth(width);
cfg.defImageHeight(height);
cfg.defImageResolution(imageResolution);
cfg.defColorModel(image->colorSpace()->colorModelId().id());
cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id());
cfg.defColorProfile(image->colorSpace()->profile()->name());
QApplication::restoreOverrideCursor();
return true;
}
KoShapeBasedDocumentBase *KisDocument::shapeController() const
{
return d->shapeController;
}
KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const
{
return d->shapeController->shapeForNode(layer);
}
vKisNodeSP KisDocument::activeNodes() const
{
vKisNodeSP nodes;
foreach(KisView *v, KisPart::instance()->views()) {
if (v->document() == this && v->viewManager()) {
KisNodeSP activeNode = v->viewManager()->activeNode();
if (activeNode && !nodes.contains(activeNode)) {
if (activeNode->inherits("KisMask")) {
activeNode = activeNode->parent();
}
nodes.append(activeNode);
}
}
}
return nodes;
}
QList<KisPaintingAssistant*> KisDocument::assistants()
{
QList<KisPaintingAssistant*> assistants;
foreach(KisView *view, KisPart::instance()->views()) {
if (view && view->document() == this) {
KisPaintingAssistantsDecoration* assistantsDecoration = view->canvasBase()->paintingAssistantsDecoration();
assistants.append(assistantsDecoration->assistants());
}
}
return assistants;
}
QList<KisPaintingAssistant *> KisDocument::preLoadedAssistants()
{
return d->assistants;
}
void KisDocument::setPreActivatedNode(KisNodeSP activatedNode)
{
d->preActivatedNode = activatedNode;
}
KisNodeSP KisDocument::preActivatedNode() const
{
return d->preActivatedNode;
}
void KisDocument::prepareForImport()
{
if (d->nserver == 0) {
init();
}
}
KisImageWSP KisDocument::image() const
{
return d->image;
}
void KisDocument::setCurrentImage(KisImageWSP image)
{
if (!image || !image.isValid()) return;
if (d->image) {
// Disconnect existing sig/slot connections
d->image->disconnect(this);
d->shapeController->setImage(0);
}
d->image = image;
d->shapeController->setImage(image);
setModified(false);
connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()));
d->image->initialRefreshGraph();
setAutoSave(KisConfig().autoSaveInterval());
}
void KisDocument::initEmpty()
{
KisConfig cfg;
const KoColorSpace * rgb = KoColorSpaceRegistry::instance()->rgb8();
newImage("", cfg.defImageWidth(), cfg.defImageHeight(), rgb);
}
void KisDocument::setImageModified()
{
setModified(true);
}
KisUndoStore* KisDocument::createUndoStore()
{
return new KisDocumentUndoStore(this);
}
#include <moc_KisDocument.cpp>
diff --git a/krita/ui/KisDocumentEntry.cpp b/krita/ui/KisDocumentEntry.cpp
index c74877c91af..91f318b1559 100644
--- a/krita/ui/KisDocumentEntry.cpp
+++ b/krita/ui/KisDocumentEntry.cpp
@@ -1,145 +1,145 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright 2007 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisDocumentEntry.h"
#include "KisPart.h"
#include "KisDocument.h"
#include <kservicetype.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KoJsonTrader.h>
#include <QPluginLoader>
#include <limits.h> // UINT_MAX
KisDocumentEntry::KisDocumentEntry()
: m_loader(0)
{
}
KisDocumentEntry::KisDocumentEntry(QPluginLoader *loader)
: m_loader(loader)
{
}
KisDocumentEntry::~KisDocumentEntry()
{
}
QString KisDocumentEntry::nativeMimeType()
{
return QString::fromLatin1(KIS_MIME_TYPE);
}
QStringList KisDocumentEntry::extraNativeMimeTypes()
{
return QStringList() << KIS_MIME_TYPE;
}
QPluginLoader * KisDocumentEntry::loader() const {
return m_loader;
}
/**
* @return TRUE if the service pointer is null
*/
bool KisDocumentEntry::isEmpty() const {
return (m_loader == 0);
}
/**
* @return name of the associated service
*/
QString KisDocumentEntry::name() const {
QJsonObject json = m_loader->metaData().value("MetaData").toObject();
json = json.value("KPlugin").toObject();
return json.value("Name").toString();
}
/**
* Mimetypes (and other service types) which this document can handle.
*/
QStringList KisDocumentEntry::mimeTypes() const {
QJsonObject json = m_loader->metaData().value("MetaData").toObject();
return json.value("MimeType").toString().split(';', QString::SkipEmptyParts);
}
/**
* @return TRUE if the document can handle the requested mimetype.
*/
bool KisDocumentEntry::supportsMimeType(const QString & _mimetype) const {
return mimeTypes().contains(_mimetype);
}
KisDocumentEntry KisDocumentEntry::queryByMimeType(const QString & mimetype)
{
QList<KisDocumentEntry> vec = query(mimetype);
if (vec.isEmpty()) {
- kWarning(30003) << "Got no results with " << mimetype;
+ warnUI << "Got no results with " << mimetype;
// Fallback to the old way (which was probably wrong, but better be safe)
vec = query(mimetype);
if (vec.isEmpty()) {
// Still no match. Either the mimetype itself is unknown, or we have no service for it.
// Help the user debugging stuff by providing some more diagnostics
if (!KServiceType::serviceType(mimetype)) {
- kError(30003) << "Unknown Calligra MimeType " << mimetype << "." << endl;
- kError(30003) << "Check your installation (for instance, run 'kde4-config --path mime' and check the result)." << endl;
+ errUI << "Unknown Calligra MimeType " << mimetype << "." << endl;
+ errUI << "Check your installation (for instance, run 'kde4-config --path mime' and check the result)." << endl;
} else {
- kError(30003) << "Found no Calligra part able to handle " << mimetype << "!" << endl;
- kError(30003) << "Check your installation (does the desktop file have X-KDE-NativeMimeType and Calligra/Part, did you install Calligra in a different prefix than KDE, without adding the prefix to /etc/kderc ?)" << endl;
+ errUI << "Found no Calligra part able to handle " << mimetype << "!" << endl;
+ errUI << "Check your installation (does the desktop file have X-KDE-NativeMimeType and Calligra/Part, did you install Calligra in a different prefix than KDE, without adding the prefix to /etc/kderc ?)" << endl;
}
return KisDocumentEntry();
}
}
// Filthy hack alert -- this'll be properly fixed in the mvc branch.
if (qApp->applicationName() == "flow" && vec.size() == 2) {
return KisDocumentEntry(vec[1]);
}
return KisDocumentEntry(vec[0]);
}
QList<KisDocumentEntry> KisDocumentEntry::query(const QString & mimetype)
{
QList<KisDocumentEntry> lst;
// Query the trader
const QList<QPluginLoader *> offers = KoJsonTrader::self()->query("Calligra/Part", mimetype);
foreach(QPluginLoader *pluginLoader, offers) {
lst.append(KisDocumentEntry(pluginLoader));
}
if (lst.count() > 1 && !mimetype.isEmpty()) {
- kWarning(30003) << "KisDocumentEntry::query " << mimetype << " got " << lst.count() << " offers!";
+ warnUI << "KisDocumentEntry::query " << mimetype << " got " << lst.count() << " offers!";
foreach(const KisDocumentEntry &entry, lst) {
- qDebug() << entry.name();
+ dbgKrita << entry.name();
}
}
return lst;
}
diff --git a/krita/ui/KisFilterChain.cpp b/krita/ui/KisFilterChain.cpp
index bd9db1750de..a96dfb22826 100644
--- a/krita/ui/KisFilterChain.cpp
+++ b/krita/ui/KisFilterChain.cpp
@@ -1,531 +1,531 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterChain.h"
#include "KisImportExportManager.h" // KisImportExportManager::filterAvailable, private API
#include "KisDocumentEntry.h"
#include "KisFilterEntry.h"
#include "KisDocument.h"
#include "KisPart.h"
#include "PriorityQueue_p.h"
#include "KisFilterGraph.h"
#include "KisFilterEdge.h"
#include "KisFilterChainLink.h"
#include "KisFilterVertex.h"
#include <QMetaMethod>
#include <QTemporaryFile>
#include <kmimetype.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <limits.h> // UINT_MAX
// Those "defines" are needed in the setupConnections method below.
// Please always keep the strings and the length in sync!
using namespace CalligraFilter;
KisFilterChain::KisFilterChain(const KisImportExportManager* manager) :
m_manager(manager), m_state(Beginning), m_inputStorage(0),
m_inputStorageDevice(0), m_outputStorage(0), m_outputStorageDevice(0),
m_inputDocument(0), m_outputDocument(0), m_inputTempFile(0),
m_outputTempFile(0), m_inputQueried(Nil), m_outputQueried(Nil), d(0)
{
}
KisFilterChain::~KisFilterChain()
{
m_chainLinks.deleteAll();
if (filterManagerParentChain() && filterManagerParentChain()->m_outputStorage)
filterManagerParentChain()->m_outputStorage->leaveDirectory();
manageIO(); // Called for the 2nd time in a row -> clean up
}
KisImportExportFilter::ConversionStatus KisFilterChain::invokeChain()
{
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
m_state = Beginning;
int count = m_chainLinks.count();
// This is needed due to nasty Microsoft design
const ChainLink* parentChainLink = 0;
if (filterManagerParentChain())
parentChainLink = filterManagerParentChain()->m_chainLinks.current();
// No iterator here, as we need m_chainLinks.current() in outputDocument()
m_chainLinks.first();
for (; count > 1 && m_chainLinks.current() && status == KisImportExportFilter::OK;
m_chainLinks.next(), --count) {
status = m_chainLinks.current()->invokeFilter(parentChainLink);
m_state = Middle;
manageIO();
}
if (!m_chainLinks.current()) {
- kWarning(30500) << "Huh?? Found a null pointer in the chain";
+ warnFile << "Huh?? Found a null pointer in the chain";
return KisImportExportFilter::StupidError;
}
if (status == KisImportExportFilter::OK) {
if (m_state & Beginning)
m_state |= End;
else
m_state = End;
status = m_chainLinks.current()->invokeFilter(parentChainLink);
manageIO();
}
m_state = Done;
if (status == KisImportExportFilter::OK)
finalizeIO();
return status;
}
QString KisFilterChain::chainOutput() const
{
if (m_state == Done)
return m_inputFile; // as we already called manageIO()
return QString();
}
QString KisFilterChain::inputFile()
{
if (m_inputQueried == File)
return m_inputFile;
else if (m_inputQueried != Nil) {
- kWarning(30500) << "You already asked for some different source.";
+ warnFile << "You already asked for some different source.";
return QString();
}
m_inputQueried = File;
if (m_state & Beginning) {
if (static_cast<KisImportExportManager::Direction>(filterManagerDirection()) ==
KisImportExportManager::Import)
m_inputFile = filterManagerImportFile();
else
inputFileHelper(filterManagerKisDocument(), filterManagerImportFile());
} else
if (m_inputFile.isEmpty())
inputFileHelper(m_inputDocument, QString());
return m_inputFile;
}
QString KisFilterChain::outputFile()
{
// sanity check: No embedded filter should ask for a plain file
// ###### CHECK: This will break as soon as we support exporting embedding filters
if (filterManagerParentChain())
- kWarning(30500) << "An embedded filter has to use storageFile()!";
+ warnFile << "An embedded filter has to use storageFile()!";
if (m_outputQueried == File)
return m_outputFile;
else if (m_outputQueried != Nil) {
- kWarning(30500) << "You already asked for some different destination.";
+ warnFile << "You already asked for some different destination.";
return QString();
}
m_outputQueried = File;
if (m_state & End) {
if (static_cast<KisImportExportManager::Direction>(filterManagerDirection()) ==
KisImportExportManager::Import)
outputFileHelper(false); // This (last) one gets deleted by the caller
else
m_outputFile = filterManagerExportFile();
} else
outputFileHelper(true);
return m_outputFile;
}
KoStoreDevice* KisFilterChain::storageFile(const QString& name, KoStore::Mode mode)
{
// Plain normal use case
if (m_inputQueried == Storage && mode == KoStore::Read &&
m_inputStorage && m_inputStorage->mode() == KoStore::Read)
return storageNewStreamHelper(&m_inputStorage, &m_inputStorageDevice, name);
else if (m_outputQueried == Storage && mode == KoStore::Write &&
m_outputStorage && m_outputStorage->mode() == KoStore::Write)
return storageNewStreamHelper(&m_outputStorage, &m_outputStorageDevice, name);
else if (m_inputQueried == Nil && mode == KoStore::Read)
return storageHelper(inputFile(), name, KoStore::Read,
&m_inputStorage, &m_inputStorageDevice);
else if (m_outputQueried == Nil && mode == KoStore::Write)
return storageHelper(outputFile(), name, KoStore::Write,
&m_outputStorage, &m_outputStorageDevice);
else {
- kWarning(30500) << "Oooops, how did we get here? You already asked for a"
+ warnFile << "Oooops, how did we get here? You already asked for a"
<< " different source/destination?" << endl;
return 0;
}
}
KisDocument* KisFilterChain::inputDocument()
{
if (m_inputQueried == Document)
return m_inputDocument;
else if (m_inputQueried != Nil) {
- kWarning(30500) << "You already asked for some different source.";
+ warnFile << "You already asked for some different source.";
return 0;
}
if ((m_state & Beginning) &&
static_cast<KisImportExportManager::Direction>(filterManagerDirection()) == KisImportExportManager::Export &&
filterManagerKisDocument())
m_inputDocument = filterManagerKisDocument();
else if (!m_inputDocument)
m_inputDocument = createDocument(inputFile());
m_inputQueried = Document;
return m_inputDocument;
}
KisDocument* KisFilterChain::outputDocument()
{
// sanity check: No embedded filter should ask for a document
// ###### CHECK: This will break as soon as we support exporting embedding filters
if (filterManagerParentChain()) {
- kWarning(30500) << "An embedded filter has to use storageFile()!";
+ warnFile << "An embedded filter has to use storageFile()!";
return 0;
}
if (m_outputQueried == Document)
return m_outputDocument;
else if (m_outputQueried != Nil) {
- kWarning(30500) << "You already asked for some different destination.";
+ warnFile << "You already asked for some different destination.";
return 0;
}
if ((m_state & End) &&
static_cast<KisImportExportManager::Direction>(filterManagerDirection()) == KisImportExportManager::Import &&
filterManagerKisDocument())
m_outputDocument = filterManagerKisDocument();
else
m_outputDocument = createDocument(m_chainLinks.current()->to());
m_outputQueried = Document;
return m_outputDocument;
}
void KisFilterChain::dump()
{
- kDebug(30500) << "########## KisFilterChain with" << m_chainLinks.count() << " members:";
+ dbgFile << "########## KisFilterChain with" << m_chainLinks.count() << " members:";
ChainLink* link = m_chainLinks.first();
while (link) {
link->dump();
link = m_chainLinks.next();
}
- kDebug(30500) << "########## KisFilterChain (done) ##########";
+ dbgFile << "########## KisFilterChain (done) ##########";
}
void KisFilterChain::appendChainLink(KisFilterEntry::Ptr filterEntry, const QByteArray& from, const QByteArray& to)
{
m_chainLinks.append(new ChainLink(this, filterEntry, from, to));
}
void KisFilterChain::prependChainLink(KisFilterEntry::Ptr filterEntry, const QByteArray& from, const QByteArray& to)
{
m_chainLinks.prepend(new ChainLink(this, filterEntry, from, to));
}
QString KisFilterChain::filterManagerImportFile() const
{
return m_manager->importFile();
}
QString KisFilterChain::filterManagerExportFile() const
{
return m_manager->exportFile();
}
KisDocument* KisFilterChain::filterManagerKisDocument() const
{
return m_manager->document();
}
int KisFilterChain::filterManagerDirection() const
{
return m_manager->direction();
}
KisFilterChain* KisFilterChain::filterManagerParentChain() const
{
return m_manager->parentChain();
}
void KisFilterChain::manageIO()
{
m_inputQueried = Nil;
m_outputQueried = Nil;
delete m_inputStorageDevice;
m_inputStorageDevice = 0;
if (m_inputStorage) {
m_inputStorage->close();
delete m_inputStorage;
m_inputStorage = 0;
}
delete m_inputTempFile; // autodelete
m_inputTempFile = 0;
m_inputFile.clear();
if (!m_outputFile.isEmpty()) {
if (m_outputTempFile == 0) {
m_inputTempFile = new QTemporaryFile;
m_inputTempFile->setAutoRemove(true);
m_inputTempFile->setFileName(m_outputFile);
}
else {
m_inputTempFile = m_outputTempFile;
m_outputTempFile = 0;
}
m_inputFile = m_outputFile;
m_outputFile.clear();
m_inputTempFile = m_outputTempFile;
m_outputTempFile = 0;
delete m_outputStorageDevice;
m_outputStorageDevice = 0;
if (m_outputStorage) {
m_outputStorage->close();
// Don't delete the storage if we're just pointing to the
// storage of the parent filter chain
if (!filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write)
delete m_outputStorage;
m_outputStorage = 0;
}
}
if (m_inputDocument != filterManagerKisDocument())
delete m_inputDocument;
m_inputDocument = m_outputDocument;
m_outputDocument = 0;
}
void KisFilterChain::finalizeIO()
{
// In case we export (to a file, of course) and the last
// filter chose to output a KisDocument we have to save it.
// Should be very rare, but well...
// Note: m_*input*Document as we already called manageIO()
if (m_inputDocument &&
static_cast<KisImportExportManager::Direction>(filterManagerDirection()) == KisImportExportManager::Export) {
- kDebug(30500) << "Saving the output document to the export file " << m_chainLinks.current()->to();
+ dbgFile << "Saving the output document to the export file " << m_chainLinks.current()->to();
m_inputDocument->setOutputMimeType(m_chainLinks.current()->to());
m_inputDocument->saveNativeFormat(filterManagerExportFile());
m_inputFile = filterManagerExportFile();
}
}
bool KisFilterChain::createTempFile(QTemporaryFile** tempFile, bool autoDelete)
{
if (*tempFile) {
- kError(30500) << "Ooops, why is there already a temp file???" << endl;
+ errFile << "Ooops, why is there already a temp file???" << endl;
return false;
}
*tempFile = new QTemporaryFile();
(*tempFile)->setAutoRemove(autoDelete);
return (*tempFile)->open();
}
/* Note about Windows & usage of QTemporaryFile
The QTemporaryFile objects m_inputTempFile and m_outputTempFile are just used
to reserve a temporary file with a unique name which then can be used to store
an intermediate format. The filters themselves do not get access to these objects,
but can query KisFilterChain only for the filename and then have to open the files
themselves with their own file handlers (TODO: change this).
On Windows this seems to be a problem and results in content not sync'ed to disk etc.
So unless someone finds out which flags might be needed on opening the files on
Windows to prevent this behaviour (unless these details are hidden away by the
Qt abstraction and cannot be influenced), a workaround is to destruct the
QTemporaryFile objects right after creation again and just take the name,
to avoid having two file handlers on the same file.
A better fix might be to use the QTemporaryFile objects also by the filters,
instead of having them open the same file on their own again, but that needs more work
and is left for... you :)
*/
void KisFilterChain::inputFileHelper(KisDocument* document, const QString& alternativeFile)
{
if (document) {
if (!createTempFile(&m_inputTempFile)) {
delete m_inputTempFile;
m_inputTempFile = 0;
m_inputFile.clear();
return;
}
m_inputFile = m_inputTempFile->fileName();
// See "Note about Windows & usage of QTemporaryFile" above
#ifdef Q_OS_WIN
m_inputTempFile->close();
m_inputTempFile->setAutoRemove(true);
delete m_inputTempFile;
m_inputTempFile = 0;
#endif
document->setOutputMimeType(m_chainLinks.current()->from());
if (!document->saveNativeFormat(m_inputFile)) {
delete m_inputTempFile;
m_inputTempFile = 0;
m_inputFile.clear();
return;
}
} else
m_inputFile = alternativeFile;
}
void KisFilterChain::outputFileHelper(bool autoDelete)
{
if (!createTempFile(&m_outputTempFile, autoDelete)) {
delete m_outputTempFile;
m_outputTempFile = 0;
m_outputFile.clear();
} else {
m_outputFile = m_outputTempFile->fileName();
// See "Note about Windows & usage of QTemporaryFile" above
#ifdef Q_OS_WIN
m_outputTempFile->close();
m_outputTempFile->setAutoRemove(true);
delete m_outputTempFile;
m_outputTempFile = 0;
#endif
}
}
KoStoreDevice* KisFilterChain::storageNewStreamHelper(KoStore** storage, KoStoreDevice** device,
const QString& name)
{
delete *device;
*device = 0;
if ((*storage)->isOpen())
(*storage)->close();
if ((*storage)->bad())
return storageCleanupHelper(storage);
if (!(*storage)->open(name))
return 0;
*device = new KoStoreDevice(*storage);
return *device;
}
KoStoreDevice* KisFilterChain::storageHelper(const QString& file, const QString& streamName,
KoStore::Mode mode, KoStore** storage,
KoStoreDevice** device)
{
if (file.isEmpty())
return 0;
if (*storage) {
- kDebug(30500) << "Uh-oh, we forgot to clean up...";
+ dbgFile << "Uh-oh, we forgot to clean up...";
return 0;
}
storageInit(file, mode, storage);
if ((*storage)->bad())
return storageCleanupHelper(storage);
// Seems that we got a valid storage, at least. Even if we can't open
// the stream the "user" asked us to open, we nonetheless change the
// IOState from File to Storage, as it might be possible to open other streams
if (mode == KoStore::Read)
m_inputQueried = Storage;
else // KoStore::Write
m_outputQueried = Storage;
return storageCreateFirstStream(streamName, storage, device);
}
void KisFilterChain::storageInit(const QString& file, KoStore::Mode mode, KoStore** storage)
{
QByteArray appIdentification("");
if (mode == KoStore::Write) {
// To create valid storages we also have to add the mimetype
// magic "applicationIndentifier" to the storage.
// As only filters with a Calligra destination should query
// for a storage to write to, we don't check the content of
// the mimetype here. It doesn't do a lot of harm if someome
// "abuses" this method.
appIdentification = m_chainLinks.current()->to();
}
*storage = KoStore::createStore(file, mode, appIdentification);
}
KoStoreDevice* KisFilterChain::storageCreateFirstStream(const QString& streamName, KoStore** storage,
KoStoreDevice** device)
{
if (!(*storage)->open(streamName))
return 0;
if (*device) {
- kDebug(30500) << "Uh-oh, we forgot to clean up the storage device!";
+ dbgFile << "Uh-oh, we forgot to clean up the storage device!";
(*storage)->close();
return storageCleanupHelper(storage);
}
*device = new KoStoreDevice(*storage);
return *device;
}
KoStoreDevice* KisFilterChain::storageCleanupHelper(KoStore** storage)
{
// Take care not to delete the storage of the parent chain
if (*storage != m_outputStorage || !filterManagerParentChain() ||
(*storage)->mode() != KoStore::Write)
delete *storage;
*storage = 0;
return 0;
}
KisDocument* KisFilterChain::createDocument(const QString& file)
{
KUrl url;
url.setPath(file);
KMimeType::Ptr t = KMimeType::findByUrl(url, 0, true);
if (t->name() == KMimeType::defaultMimeType()) {
- kError(30500) << "No mimetype found for " << file << endl;
+ errFile << "No mimetype found for " << file << endl;
return 0;
}
KisDocument *doc = createDocument(t->name().toLatin1());
if (!doc || !doc->loadNativeFormat(file)) {
- kError(30500) << "Couldn't load from the file" << endl;
+ errFile << "Couldn't load from the file" << endl;
delete doc;
return 0;
}
return doc;
}
KisDocument* KisFilterChain::createDocument(const QByteArray& mimeType)
{
Q_UNUSED(mimeType);
return KisPart::instance()->createDocument();
}
int KisFilterChain::weight() const
{
return m_chainLinks.count();
}
diff --git a/krita/ui/KisFilterChainLink.cpp b/krita/ui/KisFilterChainLink.cpp
index 7b89a2e583b..fccb4eba663 100644
--- a/krita/ui/KisFilterChainLink.cpp
+++ b/krita/ui/KisFilterChainLink.cpp
@@ -1,155 +1,155 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterChainLink.h"
#include <QMetaMethod>
#include <QPluginLoader>
#include <QTemporaryFile>
#include <kmimetype.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include "KisFilterEntry.h"
#include "KisImportExportManager.h"
#include "KoProgressUpdater.h"
#include "KoUpdater.h"
namespace
{
const char *const SIGNAL_PREFIX = "commSignal";
const int SIGNAL_PREFIX_LEN = 10;
const char *const SLOT_PREFIX = "commSlot";
const int SLOT_PREFIX_LEN = 8;
KoUpdater *createUpdater(KisFilterChain *chain)
{
QPointer<KoUpdater> updater = 0;
Q_ASSERT(chain);
Q_ASSERT(chain->manager());
KoProgressUpdater *pu = chain->manager()->progressUpdater();
if (pu) {
updater = pu->startSubtask(1, "filter");
updater->setProgress(0);
}
return updater;
}
}
namespace CalligraFilter {
ChainLink::ChainLink(KisFilterChain *chain, KisFilterEntry::Ptr filterEntry,
const QByteArray& from, const QByteArray& to)
: m_chain(chain)
, m_filterEntry(filterEntry)
, m_from(from)
, m_to(to)
, m_filter(0)
, m_updater(createUpdater(chain))
{
}
ChainLink::~ChainLink() {
}
KisImportExportFilter::ConversionStatus ChainLink::invokeFilter(const ChainLink *const parentChainLink)
{
if (!m_filterEntry) {
- kError(30500) << "This filter entry is null. Strange stuff going on." << endl;
+ errFile << "This filter entry is null. Strange stuff going on." << endl;
return KisImportExportFilter::FilterEntryNull;
}
m_filter = m_filterEntry->createFilter(m_chain);
if (!m_filter) {
- kError(30500) << "Couldn't create the filter." << endl;
+ errFile << "Couldn't create the filter." << endl;
return KisImportExportFilter::FilterCreationError;
}
if (m_updater) {
// if there is an updater, use that for progress reporting
m_filter->setUpdater(m_updater);
}
if (parentChainLink) {
setupCommunication(parentChainLink->m_filter);
}
KisImportExportFilter::ConversionStatus status = m_filter->convert(m_from, m_to);
delete m_filter;
m_filter = 0;
if (m_updater) {
m_updater->setProgress(100);
}
return status;
}
void ChainLink::dump() const
{
- kDebug(30500) << " Link:" << m_filterEntry->loader()->fileName();
+ dbgFile << " Link:" << m_filterEntry->loader()->fileName();
}
void ChainLink::setupCommunication(const KisImportExportFilter *const parentFilter) const
{
if (!parentFilter)
return;
const QMetaObject *const parent = parentFilter->metaObject();
const QMetaObject *const child = m_filter->metaObject();
if (!parent || !child)
return;
setupConnections(parentFilter, m_filter);
setupConnections(m_filter, parentFilter);
}
void ChainLink::setupConnections(const KisImportExportFilter *sender, const KisImportExportFilter *receiver) const
{
const QMetaObject * const parent = sender->metaObject();
const QMetaObject * const child = receiver->metaObject();
if (!parent || !child)
return;
int senderMethodCount = parent->methodCount();
for (int i = 0; i < senderMethodCount; ++i) {
QMetaMethod signal = parent->method(i);
if (signal.methodType() != QMetaMethod::Signal)
continue;
// ### untested (QMetaMethod::signature())
if (strncmp(signal.methodSignature(), SIGNAL_PREFIX, SIGNAL_PREFIX_LEN) == 0) {
int receiverMethodCount = child->methodCount();
for (int j = 0; j < receiverMethodCount; ++j) {
QMetaMethod slot = child->method(j);
if (slot.methodType() != QMetaMethod::Slot)
continue;
if (strncmp(slot.methodSignature(), SLOT_PREFIX, SLOT_PREFIX_LEN) == 0) {
if (strcmp(signal.methodSignature().constData() + SIGNAL_PREFIX_LEN,
slot.methodSignature().constData() + SLOT_PREFIX_LEN) == 0) {
QByteArray signalString;
signalString.setNum(QSIGNAL_CODE);
signalString += signal.methodSignature();
QByteArray slotString;
slotString.setNum(QSLOT_CODE);
slotString += slot.methodSignature();
QObject::connect(sender, signalString, receiver, slotString);
}
}
}
}
}
}
}
diff --git a/krita/ui/KisFilterEdge.cpp b/krita/ui/KisFilterEdge.cpp
index 4f50b819195..1b76b884c89 100644
--- a/krita/ui/KisFilterEdge.cpp
+++ b/krita/ui/KisFilterEdge.cpp
@@ -1,51 +1,51 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterEdge.h"
#include "KisFilterVertex.h"
#include "PriorityQueue_p.h"
namespace CalligraFilter {
Edge::Edge(Vertex* vertex, KisFilterEntry::Ptr filterEntry) :
m_vertex(vertex), m_filterEntry(filterEntry), d(0)
{
}
void Edge::relax(const Vertex* predecessor, PriorityQueue<Vertex>& queue)
{
if (!m_vertex || !predecessor || !m_filterEntry)
return;
if (m_vertex->setKey(predecessor->key() + m_filterEntry->weight)) {
queue.keyDecreased(m_vertex); // maintain the heap property
m_vertex->setPredecessor(predecessor);
}
}
void Edge::dump(const QByteArray& indent) const
{
if (m_vertex)
- kDebug(30500) << indent << "Edge -> '" << m_vertex->mimeType()
+ dbgFile << indent << "Edge -> '" << m_vertex->mimeType()
<< "' (" << m_filterEntry->weight << ")" << endl;
else
- kDebug(30500) << indent << "Edge -> '(null)' ("
+ dbgFile << indent << "Edge -> '(null)' ("
<< m_filterEntry->weight << ")" << endl;
}
}
diff --git a/krita/ui/KisFilterEntry.cpp b/krita/ui/KisFilterEntry.cpp
index 8568756636b..0cf9f131448 100644
--- a/krita/ui/KisFilterEntry.cpp
+++ b/krita/ui/KisFilterEntry.cpp
@@ -1,84 +1,84 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright 2007 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterEntry.h"
#include "KisDocument.h"
#include "KisImportExportFilter.h"
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KoJsonTrader.h>
#include <kpluginfactory.h>
#include <QFile>
#include <limits.h> // UINT_MAX
KisFilterEntry::KisFilterEntry(QPluginLoader *loader)
: m_loader(loader)
{
import = loader->metaData().value("MetaData").toObject().value("X-KDE-Import").toString().split(',');
export_ = loader->metaData().value("MetaData").toObject().value("X-KDE-Export").toString().split(',');
int w = loader->metaData().value("MetaData").toObject().value("X-KDE-Weight").toString().toInt();
weight = w < 0 ? UINT_MAX : static_cast<unsigned int>(w);
available = loader->metaData().value("MetaData").toObject().value("X-KDE-Available").toString();
}
QList<KisFilterEntry::Ptr> KisFilterEntry::query()
{
QList<KisFilterEntry::Ptr> lst;
QList<QPluginLoader *> offers = KoJsonTrader::self()->query("Krita/FileFilter", QString());
QList<QPluginLoader *>::ConstIterator it = offers.constBegin();
unsigned int max = offers.count();
- kDebug(30500) <<"Query returned" << max <<" offers";
+ dbgFile <<"Query returned" << max <<" offers";
for (unsigned int i = 0; i < max; i++) {
- //kDebug(30500) <<" desktopEntryPath=" << (*it)->entryPath()
+ //dbgFile <<" desktopEntryPath=" << (*it)->entryPath()
// << " library=" << (*it)->library() << endl;
// Append converted offer
lst.append(KisFilterEntry::Ptr(new KisFilterEntry(*it)));
// Next service
it++;
}
return lst;
}
KisImportExportFilter* KisFilterEntry::createFilter(KisFilterChain* chain, QObject* parent)
{
KLibFactory *factory = qobject_cast<KLibFactory *>(m_loader->instance());
if (!factory) {
- kWarning(30003) << m_loader->errorString();
+ warnUI << m_loader->errorString();
return 0;
}
QObject* obj = factory->create<KisImportExportFilter>(parent);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
return 0;
}
KisImportExportFilter* filter = static_cast<KisImportExportFilter*>(obj);
filter->m_chain = chain;
return filter;
}
diff --git a/krita/ui/KisFilterGraph.cpp b/krita/ui/KisFilterGraph.cpp
index d436cf845e1..0c4df36402e 100644
--- a/krita/ui/KisFilterGraph.cpp
+++ b/krita/ui/KisFilterGraph.cpp
@@ -1,245 +1,245 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterGraph.h"
#include "KisImportExportManager.h" // KisImportExportManager::filterAvailable, private API
#include "KisDocumentEntry.h"
#include "KisFilterEntry.h"
#include "KisDocument.h"
#include "PriorityQueue_p.h"
#include "KisFilterEdge.h"
#include "KisFilterChainLink.h"
#include "KisFilterVertex.h"
#include <QMetaMethod>
#include <QTemporaryFile>
#include <kmimetype.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <limits.h> // UINT_MAX
namespace CalligraFilter {
Graph::Graph(const QByteArray& from)
: m_from(from)
, m_graphValid(false)
, d(0)
{
buildGraph();
shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here)
}
Graph::~Graph()
{
foreach(Vertex* vertex, m_vertices) {
delete vertex;
}
m_vertices.clear();
}
void Graph::setSourceMimeType(const QByteArray& from)
{
if (from == m_from)
return;
m_from = from;
m_graphValid = false;
// Initialize with "infinity" ...
foreach(Vertex* vertex, m_vertices) {
vertex->reset();
}
// ...and re-run the shortest path search for the new source mime
shortestPaths();
}
KisFilterChain::Ptr Graph::chain(const KisImportExportManager* manager, QByteArray& to) const
{
if (!isValid() || !manager)
return KisFilterChain::Ptr();
if (to.isEmpty()) { // if the destination is empty we search the closest Calligra part
to = findCalligraPart();
if (to.isEmpty()) // still empty? strange stuff...
return KisFilterChain::Ptr();
}
const Vertex* vertex = m_vertices.value(to);
if (!vertex || vertex->key() == UINT_MAX)
return KisFilterChain::Ptr();
KisFilterChain::Ptr ret(new KisFilterChain(manager));
// Fill the filter chain with all filters on the path
const Vertex* tmp = vertex->predecessor();
while (tmp) {
const Edge* const edge = tmp->findEdge(vertex);
Q_ASSERT(edge);
ret->prependChainLink(edge->filterEntry(), tmp->mimeType(), vertex->mimeType());
vertex = tmp;
tmp = tmp->predecessor();
}
return ret;
}
void Graph::dump() const
{
#ifndef NDEBUG
- kDebug(30500) << "+++++++++ Graph::dump +++++++++";
- kDebug(30500) << "From:" << m_from;
+ dbgFile << "+++++++++ Graph::dump +++++++++";
+ dbgFile << "From:" << m_from;
foreach(Vertex *vertex, m_vertices) {
vertex->dump(" ");
}
- kDebug(30500) << "+++++++++ Graph::dump (done) +++++++++";
+ dbgFile << "+++++++++ Graph::dump (done) +++++++++";
#endif
}
// Query the trader and create the vertices and edges representing
// available mime types and filters.
void Graph::buildGraph()
{
// Make sure that all available parts are added to the graph
const QList<KisDocumentEntry> parts(KisDocumentEntry::query());
foreach(const KisDocumentEntry& part, parts) {
QStringList nativeMimeTypes = part.loader()->metaData().value("MetaData").toObject().value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
nativeMimeTypes += part.loader()->metaData().value("MetaData").toObject().value("X-KDE-NativeMimeType").toString();
foreach(const QString& nativeMimeType, nativeMimeTypes) {
const QByteArray key = nativeMimeType.toLatin1();
if (!m_vertices.contains(key))
m_vertices[key] = new Vertex(key);
}
}
// no constraint here - we want *all* :)
const QList<KisFilterEntry::Ptr> filters(KisFilterEntry::query());
foreach(KisFilterEntry::Ptr filter, filters) {
// First add the "starting points" to the dict
foreach (const QString& import, filter->import) {
const QByteArray key = import.toLatin1(); // latin1 is okay here (werner)
// already there?
if (!m_vertices.contains(key))
m_vertices.insert(key, new Vertex(key));
}
// Are we allowed to use this filter at all?
if (KisImportExportManager::filterAvailable(filter)) {
foreach(const QString& exportIt, filter->export_) {
// First make sure the export vertex is in place
const QByteArray key = exportIt.toLatin1(); // latin1 is okay here
Vertex* exp = m_vertices.value(key);
if (!exp) {
exp = new Vertex(key);
m_vertices.insert(key, exp);
}
// Then create the appropriate edges
foreach(const QString& import, filter->import) {
m_vertices[import.toLatin1()]->addEdge(new Edge(exp, filter));
}
}
} else
- kDebug(30500) << "Filter:" << filter->loader()->fileName() << " doesn't apply.";
+ dbgFile << "Filter:" << filter->loader()->fileName() << " doesn't apply.";
}
}
// As all edges (=filters) are required to have a positive weight
// we can use Dijkstra's shortest path algorithm from Cormen's
// "Introduction to Algorithms" (p. 527)
// Note: I did some adaptions as our data structures are slightly
// different from the ones used in the book. Further we simply stop
// the algorithm is we don't find any node with a weight != Infinity
// (==UINT_MAX), as this means that the remaining nodes in the queue
// aren't connected anyway.
void Graph::shortestPaths()
{
// Is the requested start mime type valid?
Vertex* from = m_vertices.value(m_from);
if (!from)
return;
// Inititalize start vertex
from->setKey(0);
// Fill the priority queue with all the vertices
PriorityQueue<Vertex> queue(m_vertices);
while (!queue.isEmpty()) {
Vertex *min = queue.extractMinimum();
// Did we already relax all connected vertices?
if (min->key() == UINT_MAX)
break;
min->relaxVertices(queue);
}
m_graphValid = true;
}
QByteArray Graph::findCalligraPart() const
{
// Here we simply try to find the closest Calligra mimetype
const QList<KisDocumentEntry> parts(KisDocumentEntry::query());
QList<KisDocumentEntry>::ConstIterator partIt(parts.constBegin());
QList<KisDocumentEntry>::ConstIterator partEnd(parts.constEnd());
const Vertex *v = 0;
// Be sure that v gets initialized correctly
while (!v && partIt != partEnd) {
QStringList nativeMimeTypes = (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
nativeMimeTypes += (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-NativeMimeType").toString();
QStringList::ConstIterator it = nativeMimeTypes.constBegin();
QStringList::ConstIterator end = nativeMimeTypes.constEnd();
for (; !v && it != end; ++it)
if (!(*it).isEmpty())
v = m_vertices.value((*it).toLatin1());
++partIt;
}
if (!v)
return "";
// Now we try to find the "cheapest" Calligra vertex
while (partIt != partEnd) {
QStringList nativeMimeTypes = (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
nativeMimeTypes += (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-NativeMimeType").toString();
QStringList::ConstIterator it = nativeMimeTypes.constBegin();
QStringList::ConstIterator end = nativeMimeTypes.constEnd();
for (; !v && it != end; ++it) {
QString key = *it;
if (!key.isEmpty()) {
Vertex* tmp = m_vertices.value(key.toLatin1());
if (!v || (tmp && tmp->key() < v->key()))
v = tmp;
}
}
++partIt;
}
// It seems it already is a Calligra part
if (v->key() == 0)
return "";
return v->mimeType();
}
}
diff --git a/krita/ui/KisFilterVertex.cpp b/krita/ui/KisFilterVertex.cpp
index b37f58a4ed5..9259bd0d2b1 100644
--- a/krita/ui/KisFilterVertex.cpp
+++ b/krita/ui/KisFilterVertex.cpp
@@ -1,95 +1,95 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisFilterVertex.h"
#include <limits.h> // UINT_MAX
#include "KisFilterEdge.h"
namespace CalligraFilter {
Vertex::Vertex(const QByteArray& mimeType)
: m_predecessor(0)
, m_mimeType(mimeType)
, m_weight(UINT_MAX)
, m_index(-1)
, d(0)
{
}
Vertex::~Vertex()
{
qDeleteAll(m_edges);
}
bool Vertex::setKey(unsigned int key)
{
if (m_weight > key) {
m_weight = key;
return true;
}
return false;
}
void Vertex::reset()
{
m_weight = UINT_MAX;
m_predecessor = 0;
}
void Vertex::addEdge(Edge* edge)
{
if (!edge || edge->weight() == 0)
return;
m_edges.append(edge);
}
const Edge* Vertex::findEdge(const Vertex* vertex) const
{
if (!vertex)
return 0;
const Edge* edge = 0;
foreach(Edge* e, m_edges) {
if (e->vertex() == vertex &&
(!edge || e->weight() < edge->weight())) {
edge = e;
}
}
return edge;
}
void Vertex::relaxVertices(PriorityQueue<Vertex>& queue)
{
foreach(Edge* e, m_edges) {
e->relax(this, queue);
}
}
void Vertex::dump(const QByteArray& indent) const
{
#ifdef NDEBUG
Q_UNUSED(indent)
#else
- kDebug(30500) << indent << "Vertex:" << m_mimeType << " (" << m_weight << "):";
+ dbgFile << indent << "Vertex:" << m_mimeType << " (" << m_weight << "):";
const QByteArray i(indent + " ");
foreach(Edge* edge, m_edges) {
edge->dump(i);
}
#endif
}
}
diff --git a/krita/ui/KisImportExportFilter.cpp b/krita/ui/KisImportExportFilter.cpp
index 4238d42b8c9..8c95e133273 100644
--- a/krita/ui/KisImportExportFilter.cpp
+++ b/krita/ui/KisImportExportFilter.cpp
@@ -1,68 +1,68 @@
/* This file is part of the KDE libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
2002 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisImportExportFilter.h"
#include <QFile>
#include <kurl.h>
#include <kmimetype.h>
#include <QTemporaryFile>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <QStack>
#include "KisImportExportManager.h"
#include "KoUpdater.h"
class Q_DECL_HIDDEN KisImportExportFilter::Private
{
public:
QPointer<KoUpdater> updater;
Private() :updater(0) {}
};
KisImportExportFilter::KisImportExportFilter(QObject *parent)
: QObject(parent), m_chain(0), d(new Private)
{
}
KisImportExportFilter::~KisImportExportFilter()
{
if (d->updater) d->updater->setProgress(100);
delete d;
}
void KisImportExportFilter::setUpdater(const QPointer<KoUpdater>& updater)
{
if (d->updater && !updater) {
disconnect(this, SLOT(slotProgress(int)));
} else if (!d->updater && updater) {
connect(this, SIGNAL(sigProgress(int)), SLOT(slotProgress(int)));
}
d->updater = updater;
}
void KisImportExportFilter::slotProgress(int value)
{
if (d->updater) {
d->updater->setValue(value);
}
}
#include <KisImportExportFilter.moc>
diff --git a/krita/ui/KisImportExportManager.cpp b/krita/ui/KisImportExportManager.cpp
index fc37b2c6633..2a12603aae6 100644
--- a/krita/ui/KisImportExportManager.cpp
+++ b/krita/ui/KisImportExportManager.cpp
@@ -1,559 +1,559 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
2000, 2001 Werner Trobin <trobin@kde.org>
Copyright (C) 2004 Nicolas Goutte <goutte@kde.org>
Copyright (C) 2009 Thomas Zander <zander@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisImportExportManager.h"
#include "KisImportExportManager_p.h"
#include "KisDocument.h"
#include "KisDocumentEntry.h"
#include "KoProgressUpdater.h"
#include <QFile>
#include <QLabel>
#include <QVBoxLayout>
#include <QList>
#include <QApplication>
#include <QByteArray>
#include <klocale.h>
#include <QMessageBox>
#include <klibloader.h>
#include <ksqueezedtextlabel.h>
#include <kmimetype.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <queue>
#include <unistd.h>
// static cache for filter availability
QMap<QString, bool> KisImportExportManager::m_filterAvailable;
KisImportExportManager::KisImportExportManager(KisDocument* document,
KoProgressUpdater* progressUpdater) :
m_document(document), m_parentChain(0), m_graph(""),
d(new Private(progressUpdater))
{
d->batch = false;
}
KisImportExportManager::KisImportExportManager(const QString& url, const QByteArray& mimetypeHint,
KisFilterChain* const parentChain) :
m_document(0), m_parentChain(parentChain), m_importUrl(url), m_importUrlMimetypeHint(mimetypeHint),
m_graph(""), d(new Private)
{
d->batch = false;
}
KisImportExportManager::KisImportExportManager(const QByteArray& mimeType) :
m_document(0), m_parentChain(0), m_graph(""), d(new Private)
{
d->batch = false;
d->importMimeType = mimeType;
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
QString KisImportExportManager::importDocument(const QString& url,
const QString& documentMimeType,
KisImportExportFilter::ConversionStatus& status)
{
// Find the mime type for the file to be imported.
QString typeName(documentMimeType);
KUrl u(url);
KMimeType::Ptr t;
if (documentMimeType.isEmpty()) {
t = KMimeType::findByUrl(u, 0, true);
if (t)
typeName = t->name();
}
m_graph.setSourceMimeType(typeName.toLatin1()); // .latin1() is okay here (Werner)
if (!m_graph.isValid()) {
bool userCancelled = false;
- kWarning(30500) << "Can't open " << typeName << ", trying filter chooser";
+ warnFile << "Can't open " << typeName << ", trying filter chooser";
if (m_document) {
if (!m_document->isAutoErrorHandlingEnabled()) {
status = KisImportExportFilter::BadConversionGraph;
return QString();
}
QByteArray nativeFormat = m_document->nativeFormatMimeType();
QApplication::setOverrideCursor(Qt::ArrowCursor);
KisFilterChooser chooser(0,
KisImportExportManager::mimeFilter(nativeFormat, KisImportExportManager::Import,
m_document->extraNativeMimeTypes()), nativeFormat, u);
if (chooser.exec()) {
QByteArray f = chooser.filterSelected().toLatin1();
if (f == nativeFormat) {
status = KisImportExportFilter::OK;
QApplication::restoreOverrideCursor();
return url;
}
m_graph.setSourceMimeType(f);
} else
userCancelled = true;
QApplication::restoreOverrideCursor();
}
if (!m_graph.isValid()) {
- kError(30500) << "Couldn't create a valid graph for this source mimetype: "
+ errFile << "Couldn't create a valid graph for this source mimetype: "
<< typeName;
importErrorHelper(typeName, userCancelled);
status = KisImportExportFilter::BadConversionGraph;
return QString();
}
}
KisFilterChain::Ptr chain(0);
// Are we owned by a KisDocument?
if (m_document) {
QByteArray mimeType = m_document->nativeFormatMimeType();
QStringList extraMimes = m_document->extraNativeMimeTypes();
int i = 0;
int n = extraMimes.count();
chain = m_graph.chain(this, mimeType);
while (i < n) {
QByteArray extraMime = extraMimes[i].toUtf8();
// TODO check if its the same target mime then continue
KisFilterChain::Ptr newChain(0);
newChain = m_graph.chain(this, extraMime);
if (!chain || (newChain && newChain->weight() < chain->weight()))
chain = newChain;
++i;
}
} else if (!d->importMimeType.isEmpty()) {
chain = m_graph.chain(this, d->importMimeType);
} else {
- kError(30500) << "You aren't supposed to use import() from a filter!" << endl;
+ errFile << "You aren't supposed to use import() from a filter!" << endl;
status = KisImportExportFilter::UsageError;
return QString();
}
if (!chain) {
- kError(30500) << "Couldn't create a valid filter chain!" << endl;
+ errFile << "Couldn't create a valid filter chain!" << endl;
importErrorHelper(typeName);
status = KisImportExportFilter::BadConversionGraph;
return QString();
}
// Okay, let's invoke the filters one after the other
m_direction = Import; // vital information!
m_importUrl = url; // We want to load that file
m_exportUrl.clear(); // This is null for sure, as embedded stuff isn't
// allowed to use that method
status = chain->invokeChain();
m_importUrl.clear(); // Reset the import URL
if (status == KisImportExportFilter::OK)
return chain->chainOutput();
return QString();
}
KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& url, QByteArray& mimeType)
{
bool userCancelled = false;
// The import url should already be set correctly (null if we have a KisDocument
// file manager and to the correct URL if we have an embedded manager)
m_direction = Export; // vital information!
m_exportUrl = url;
KisFilterChain::Ptr chain;
if (m_document) {
// We have to pick the right native mimetype as source.
QStringList nativeMimeTypes;
nativeMimeTypes.append(m_document->nativeFormatMimeType());
nativeMimeTypes += m_document->extraNativeMimeTypes();
QStringList::ConstIterator it = nativeMimeTypes.constBegin();
const QStringList::ConstIterator end = nativeMimeTypes.constEnd();
for (; !chain && it != end; ++it) {
m_graph.setSourceMimeType((*it).toLatin1());
if (m_graph.isValid())
chain = m_graph.chain(this, mimeType);
}
} else if (!m_importUrlMimetypeHint.isEmpty()) {
- kDebug(30500) << "Using the mimetype hint:" << m_importUrlMimetypeHint;
+ dbgFile << "Using the mimetype hint:" << m_importUrlMimetypeHint;
m_graph.setSourceMimeType(m_importUrlMimetypeHint);
} else {
KUrl u;
u.setPath(m_importUrl);
KMimeType::Ptr t = KMimeType::findByUrl(u, 0, true);
if (!t || t->name() == KMimeType::defaultMimeType()) {
- kError(30500) << "No mimetype found for" << m_importUrl;
+ errFile << "No mimetype found for" << m_importUrl;
return KisImportExportFilter::BadMimeType;
}
m_graph.setSourceMimeType(t->name().toLatin1());
if (!m_graph.isValid()) {
- kWarning(30500) << "Can't open" << t->name() << ", trying filter chooser";
+ warnFile << "Can't open" << t->name() << ", trying filter chooser";
QApplication::setOverrideCursor(Qt::ArrowCursor);
KisFilterChooser chooser(0, KisImportExportManager::mimeFilter(), QString(), u);
if (chooser.exec())
m_graph.setSourceMimeType(chooser.filterSelected().toLatin1());
else
userCancelled = true;
QApplication::restoreOverrideCursor();
}
}
if (!m_graph.isValid()) {
- kError(30500) << "Couldn't create a valid graph for this source mimetype.";
+ errFile << "Couldn't create a valid graph for this source mimetype.";
if (!d->batch && !userCancelled) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not export file: the export filter is missing."));
}
return KisImportExportFilter::BadConversionGraph;
}
if (!chain) // already set when coming from the m_document case
chain = m_graph.chain(this, mimeType);
if (!chain) {
- kError(30500) << "Couldn't create a valid filter chain to " << mimeType << " !" << endl;
+ errFile << "Couldn't create a valid filter chain to " << mimeType << " !" << endl;
if (!d->batch) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not export file: the export filter is missing."));
}
return KisImportExportFilter::BadConversionGraph;
}
return chain->invokeChain();
}
namespace // in order not to mess with the global namespace ;)
{
// This class is needed only for the static mimeFilter method
class Vertex
{
public:
Vertex(const QByteArray& mimeType) : m_color(White), m_mimeType(mimeType) {}
enum Color { White, Gray, Black };
Color color() const {
return m_color;
}
void setColor(Color color) {
m_color = color;
}
QByteArray mimeType() const {
return m_mimeType;
}
void addEdge(Vertex* vertex) {
if (vertex) m_edges.append(vertex);
}
QList<Vertex*> edges() const {
return m_edges;
}
private:
Color m_color;
QByteArray m_mimeType;
QList<Vertex*> m_edges;
};
// Some helper methods for the static stuff
// This method builds up the graph in the passed ascii dict
void buildGraph(QHash<QByteArray, Vertex*>& vertices, KisImportExportManager::Direction direction)
{
QStringList stopList; // Lists of mimetypes that are considered end of chains
stopList << "text/plain";
stopList << "text/csv";
stopList << "text/x-tex";
stopList << "text/html";
// partly copied from build graph, but I don't see any other
// way without crude hacks, as we have to obey the direction here
QList<KisDocumentEntry> parts(KisDocumentEntry::query(QString()));
QList<KisDocumentEntry>::ConstIterator partIt(parts.constBegin());
QList<KisDocumentEntry>::ConstIterator partEnd(parts.constEnd());
while (partIt != partEnd) {
QStringList nativeMimeTypes = (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
nativeMimeTypes += (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-NativeMimeType").toString();
QStringList::ConstIterator it = nativeMimeTypes.constBegin();
const QStringList::ConstIterator end = nativeMimeTypes.constEnd();
for (; it != end; ++it)
if (!(*it).isEmpty()) {
const QByteArray key = (*it).toLatin1();
if (!vertices.contains(key))
vertices.insert(key, new Vertex(key));
}
++partIt;
}
QList<KisFilterEntry::Ptr> filters = KisFilterEntry::query(); // no constraint here - we want *all* :)
QList<KisFilterEntry::Ptr>::ConstIterator it = filters.constBegin();
QList<KisFilterEntry::Ptr>::ConstIterator end = filters.constEnd();
foreach(KisFilterEntry::Ptr filterEntry, filters)
for (; it != end; ++it) {
QStringList impList; // Import list
QStringList expList; // Export list
// Now we have to exclude the "stop" mimetypes (in the right direction!)
if (direction == KisImportExportManager::Import) {
// Import: "stop" mime type should not appear in export
foreach(const QString & testIt, (*it)->export_) {
if (!stopList.contains(testIt))
expList.append(testIt);
}
impList = (*it)->import;
} else {
// Export: "stop" mime type should not appear in import
foreach(const QString & testIt, (*it)->import) {
if (!stopList.contains(testIt))
impList.append(testIt);
}
expList = (*it)->export_;
}
if (impList.empty() || expList.empty()) {
// This filter cannot be used under these conditions
- kDebug(30500) << "Filter:" << (*it)->loader()->fileName() << " ruled out";
+ dbgFile << "Filter:" << (*it)->loader()->fileName() << " ruled out";
continue;
}
// First add the "starting points" to the dict
QStringList::ConstIterator importIt = impList.constBegin();
const QStringList::ConstIterator importEnd = impList.constEnd();
for (; importIt != importEnd; ++importIt) {
const QByteArray key = (*importIt).toLatin1(); // latin1 is okay here (werner)
// already there?
if (!vertices[ key ])
vertices.insert(key, new Vertex(key));
}
// Are we allowed to use this filter at all?
if (KisImportExportManager::filterAvailable(*it)) {
QStringList::ConstIterator exportIt = expList.constBegin();
const QStringList::ConstIterator exportEnd = expList.constEnd();
for (; exportIt != exportEnd; ++exportIt) {
// First make sure the export vertex is in place
const QByteArray key = (*exportIt).toLatin1(); // latin1 is okay here
Vertex* exp = vertices[ key ];
if (!exp) {
exp = new Vertex(key);
vertices.insert(key, exp);
}
// Then create the appropriate edges depending on the
// direction (import/export)
// This is the chunk of code which actually differs from the
// graph stuff (apart from the different vertex class)
importIt = impList.constBegin(); // ### TODO: why only the first one?
if (direction == KisImportExportManager::Import) {
for (; importIt != importEnd; ++importIt)
exp->addEdge(vertices[(*importIt).toLatin1()]);
} else {
for (; importIt != importEnd; ++importIt)
vertices[(*importIt).toLatin1()]->addEdge(exp);
}
}
} else {
- kDebug(30500) << "Filter:" << (*it)->loader()->fileName() << " does not apply.";
+ dbgFile << "Filter:" << (*it)->loader()->fileName() << " does not apply.";
}
}
}
// This method runs a BFS on the graph to determine the connected
// nodes. Make sure that the graph is "cleared" (the colors of the
// nodes are all white)
QStringList connected(const QHash<QByteArray, Vertex*>& vertices, const QByteArray& mimetype)
{
if (mimetype.isEmpty())
return QStringList();
Vertex *v = vertices[ mimetype ];
if (!v)
return QStringList();
v->setColor(Vertex::Gray);
std::queue<Vertex*> queue;
queue.push(v);
QStringList connected;
while (!queue.empty()) {
v = queue.front();
queue.pop();
QList<Vertex*> edges = v->edges();
foreach(Vertex* current, edges) {
if (current->color() == Vertex::White) {
current->setColor(Vertex::Gray);
queue.push(current);
}
}
v->setColor(Vertex::Black);
connected.append(v->mimeType());
}
return connected;
}
} // anon namespace
// The static method to figure out to which parts of the
// graph this mimetype has a connection to.
QStringList KisImportExportManager::mimeFilter(const QByteArray &mimetype, Direction direction, const QStringList &extraNativeMimeTypes)
{
- //kDebug(30500) <<"mimetype=" << mimetype <<" extraNativeMimeTypes=" << extraNativeMimeTypes;
+ //dbgFile <<"mimetype=" << mimetype <<" extraNativeMimeTypes=" << extraNativeMimeTypes;
QHash<QByteArray, Vertex*> vertices;
buildGraph(vertices, direction);
// TODO maybe use the fake vertex trick from the method below, to make the search faster?
QStringList nativeMimeTypes;
nativeMimeTypes.append(QString::fromLatin1(mimetype));
nativeMimeTypes += extraNativeMimeTypes;
// Add the native mimetypes first so that they are on top.
QStringList lst = nativeMimeTypes;
// Now look for filters which output each of those natives mimetypes
foreach(const QString &natit, nativeMimeTypes) {
const QStringList outMimes = connected(vertices, natit.toLatin1());
- //kDebug(30500) <<"output formats connected to mime" << natit <<" :" << outMimes;
+ //dbgFile <<"output formats connected to mime" << natit <<" :" << outMimes;
foreach(const QString &mit, outMimes) {
if (!lst.contains(mit)) // append only if not there already. Qt4: QSet<QString>?
lst.append(mit);
}
}
foreach(Vertex* vertex, vertices) {
delete vertex;
}
vertices.clear();
return lst;
}
QStringList KisImportExportManager::mimeFilter()
{
QHash<QByteArray, Vertex*> vertices;
buildGraph(vertices, KisImportExportManager::Import);
QList<KisDocumentEntry> parts(KisDocumentEntry::query( QString()));
QList<KisDocumentEntry>::ConstIterator partIt(parts.constBegin());
QList<KisDocumentEntry>::ConstIterator partEnd(parts.constEnd());
if (partIt == partEnd)
return QStringList();
// To find *all* reachable mimetypes, we have to resort to
// a small hat trick, in order to avoid multiple searches:
// We introduce a fake vertex, which is connected to every
// single Calligra mimetype. Due to that one BFS is enough :)
// Now we just need an... ehrm.. unique name for our fake mimetype
Vertex *v = new Vertex("supercalifragilistic/x-pialadocious");
vertices.insert("supercalifragilistic/x-pialadocious", v);
while (partIt != partEnd) {
QStringList nativeMimeTypes = (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-ExtraNativeMimeTypes").toString().split(',');
nativeMimeTypes += (*partIt).loader()->metaData().value("MetaData").toObject().value("X-KDE-NativeMimeType").toString();
QStringList::ConstIterator it = nativeMimeTypes.constBegin();
const QStringList::ConstIterator end = nativeMimeTypes.constEnd();
for (; it != end; ++it)
if (!(*it).isEmpty())
v->addEdge(vertices[(*it).toLatin1()]);
++partIt;
}
QStringList result = connected(vertices, "supercalifragilistic/x-pialadocious");
// Finally we have to get rid of our fake mimetype again
result.removeAll("supercalifragilistic/x-pialadocious");
return result;
}
// Here we check whether the filter is available. This stuff is quite slow,
// but I don't see any other convenient (for the user) way out :}
bool KisImportExportManager::filterAvailable(KisFilterEntry::Ptr entry)
{
if (!entry)
return false;
if (entry->available != "check")
return true;
// QT5TODO
#if 1
return true;
#else
- //kDebug( 30500 ) <<"Checking whether" << entry->service()->name() <<" applies.";
+ //dbgFile <<"Checking whether" << entry->service()->name() <<" applies.";
// generate some "unique" key
QString key = entry->service()->name() + " - " + entry->service()->library();
if (!m_filterAvailable.contains(key)) {
- //kDebug( 30500 ) <<"Not cached, checking...";
+ //dbgFile <<"Not cached, checking...";
KLibrary library(QFile::encodeName(entry->service()->library()));
if (library.fileName().isEmpty()) {
- kWarning(30500) << "Huh?? Couldn't load the lib: "
+ warnFile << "Huh?? Couldn't load the lib: "
<< entry->service()->library();
m_filterAvailable[ key ] = false;
return false;
}
// This code is "borrowed" from klibloader ;)
QByteArray symname = "check_" + QString(library.objectName()).toLatin1();
KLibrary::void_function_ptr sym = library.resolveFunction(symname);
if (!sym) {
-// kWarning(30500) << "The library " << library.objectName()
+// warnFile << "The library " << library.objectName()
// << " does not offer a check_" << library.objectName()
// << " function." << endl;
m_filterAvailable[key] = false;
} else {
typedef int (*t_func)();
t_func check = (t_func)sym;
m_filterAvailable[ key ] = check() == 1;
}
}
return m_filterAvailable[key];
#endif
}
void KisImportExportManager::importErrorHelper(const QString& mimeType, const bool suppressDialog)
{
// ###### FIXME: use KLibLoader::lastErrorMessage() here
if (!suppressDialog) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not import file of type\n%1. The import filter is missing.", mimeType));
}
}
void KisImportExportManager::setBatchMode(const bool batch)
{
d->batch = batch;
}
bool KisImportExportManager::getBatchMode(void) const
{
return d->batch;
}
KoProgressUpdater* KisImportExportManager::progressUpdater() const
{
if (d->progressUpdater.isNull()) {
// somebody, probably its parent, deleted our progress updater for us
return 0;
}
return d->progressUpdater.data();
}
#include <KisImportExportManager.moc>
diff --git a/krita/ui/KisMainWindow.cpp b/krita/ui/KisMainWindow.cpp
index ddcafe5c3c8..d666c1d58da 100644
--- a/krita/ui/KisMainWindow.cpp
+++ b/krita/ui/KisMainWindow.cpp
@@ -1,2237 +1,2240 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2006 David Faure <faure@kde.org>
Copyright (C) 2007, 2009 Thomas zander <zander@kde.org>
Copyright (C) 2010 Benjamin Port <port.benjamin@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h"
// qt includes
#include <QApplication>
#include <QByteArray>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDockWidget>
#include <QIcon>
#include <QLabel>
#include <QLayout>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QMutex>
#include <QMutexLocker>
#include <QPointer>
#include <QPrintDialog>
#include <QPrinter>
#include <QPrintPreviewDialog>
#include <QProgressBar>
#include <QSignalMapper>
#include <QTabBar>
#include <QMoveEvent>
#include <krecentdirs.h>
#include <kaboutdata.h>
#include <kactioncollection.h>
#include <kaction.h>
#include <kactionmenu.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kdiroperator.h>
#include <kedittoolbar.h>
#include <kfileitem.h>
#include <kglobalsettings.h>
#include <khelpmenu.h>
#include <klocale.h>
#include <kmenubar.h>
#include <kmenu.h>
#include <QMessageBox>
#include <kmimetype.h>
#include <krecentdocument.h>
#include <krecentfilesaction.h>
#include <kstandarddirs.h>
#include <kstatusbar.h>
#include <QTemporaryFile>
#include <ktoggleaction.h>
#include <ktoolbar.h>
#include <kurlcombobox.h>
#include <kurl.h>
#include <kmainwindow.h>
#include <kxmlguiwindow.h>
#include <kxmlguifactory.h>
#include <kxmlguiclient.h>
#include <kguiitem.h>
#include <k4aboutdata.h>
#include <KoConfig.h>
#include "KoDockFactoryBase.h"
#include "KoDockWidgetTitleBar.h"
#include "KoDocumentInfoDlg.h"
#include "KoDocumentInfo.h"
#include "KoFileDialog.h"
#include <KoIcon.h>
#include <KoPageLayoutDialog.h>
#include <KoPageLayoutWidget.h>
#include <KoToolManager.h>
#include <KoZoomController.h>
#include "KoToolDocker.h"
#include <KoToolBoxFactory.h>
#include <KoDockRegistry.h>
#include <KoPluginLoader.h>
#include <KoColorSpaceEngine.h>
#include "KisView.h"
#include "KisDocument.h"
#include "KisImportExportManager.h"
#include "KisPrintJob.h"
#include "KisPart.h"
#include "KisApplication.h"
#include "kis_factory2.h"
#include "kis_action.h"
#include "kis_canvas_controller.h"
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "KisDocument.h"
#include "KisView.h"
#include "dialogs/kis_dlg_preferences.h"
#include "kis_config_notifier.h"
#include "kis_canvas_resource_provider.h"
#include "kis_node.h"
#include "kis_image.h"
#include "kis_group_layer.h"
#include "kis_paintop_settings.h"
#include "kis_paintop_box.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "dialogs/kis_about_application.h"
#include "kis_mainwindow_observer.h"
#include "kis_action_manager.h"
#include "thememanager.h"
#include "kis_resource_server_provider.h"
#include "kis_icon_utils.h"
-
+#include <KisImportExportFilter.h>
+#include <KisDocumentEntry.h>
#include "calligraversion.h"
class ToolDockerFactory : public KoDockFactoryBase
{
public:
ToolDockerFactory() : KoDockFactoryBase() { }
QString id() const {
return "sharedtooldocker";
}
QDockWidget* createDockWidget() {
KoToolDocker* dockWidget = new KoToolDocker();
dockWidget->setTabEnabled(false);
return dockWidget;
}
DockPosition defaultDockPosition() const {
return DockRight;
}
};
class Q_DECL_HIDDEN KisMainWindow::Private
{
public:
Private(KisMainWindow *parent)
: viewManager(0)
, firstTime(true)
, windowSizeDirty(false)
, readOnly(false)
, isImporting(false)
, isExporting(false)
, noCleanup(false)
, showDocumentInfo(0)
, saveAction(0)
, saveActionAs(0)
, printAction(0)
, printActionPreview(0)
, exportPdf(0)
, closeAll(0)
// , reloadFile(0)
, importFile(0)
, exportFile(0)
, undo(0)
, redo(0)
, newWindow(0)
, close(0)
, mdiCascade(0)
, mdiTile(0)
, mdiNextWindow(0)
, mdiPreviousWindow(0)
, toggleDockers(0)
, toggleDockerTitleBars(0)
, dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent))
, windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent))
, documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent))
, helpMenu(0)
, brushesAndStuff(0)
, recentFiles(0)
, toolOptionsDocker(0)
, deferredClosingEvent(0)
, themeManager(0)
, mdiArea(new QMdiArea(parent))
, activeSubWindow(0)
, windowMapper(new QSignalMapper(parent))
, documentMapper(new QSignalMapper(parent))
, lastExportSpecialOutputFlag(0)
{
}
~Private() {
qDeleteAll(toolbarList);
}
KisViewManager *viewManager;
QPointer<KisView> activeView;
QPointer<QProgressBar> progress;
QMutex progressMutex;
QList<QAction *> toolbarList;
bool firstTime;
bool windowSizeDirty;
bool readOnly;
bool isImporting;
bool isExporting;
bool noCleanup;
KisAction *showDocumentInfo;
KisAction *saveAction;
KisAction *saveActionAs;
KisAction *printAction;
KisAction *printActionPreview;
KisAction *exportPdf;
KisAction *closeAll;
// KisAction *reloadFile;
KisAction *importFile;
KisAction *exportFile;
KisAction *undo;
KisAction *redo;
KisAction *newWindow;
KisAction *close;
KisAction *mdiCascade;
KisAction *mdiTile;
KisAction *mdiNextWindow;
KisAction *mdiPreviousWindow;
KisAction *toggleDockers;
KisAction *toggleDockerTitleBars;
KisAction *expandingSpacers[2];
KActionMenu *dockWidgetMenu;
KActionMenu *windowMenu;
KActionMenu *documentMenu;
KHelpMenu *helpMenu;
KToolBar *brushesAndStuff;
KRecentFilesAction *recentFiles;
KUrl lastExportUrl;
QMap<QString, QDockWidget *> dockWidgetsMap;
QMap<QDockWidget *, bool> dockWidgetVisibilityMap;
QByteArray dockerStateBeforeHiding;
KoToolDocker *toolOptionsDocker;
QCloseEvent *deferredClosingEvent;
Digikam::ThemeManager *themeManager;
QMdiArea *mdiArea;
QMdiSubWindow *activeSubWindow;
QSignalMapper *windowMapper;
QSignalMapper *documentMapper;
QByteArray lastExportedFormat;
int lastExportSpecialOutputFlag;
};
KisMainWindow::KisMainWindow()
: KXmlGuiWindow()
, d(new Private(this))
{
// setComponentData(KisFactory::componentData());
KGlobal::setActiveComponent(KisFactory::componentData());
KisConfig cfg;
d->viewManager = new KisViewManager(this, actionCollection());
d->themeManager = new Digikam::ThemeManager(this);
setAcceptDrops(true);
setStandardToolBarMenuEnabled(true);
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
setDockNestingEnabled(true);
qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events
#ifdef Q_OS_MAC
setUnifiedTitleAndToolBarOnMac(true);
#endif
connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts()));
connect(this, SIGNAL(documentSaved()), d->viewManager, SLOT(slotDocumentSaved()));
connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons()));
connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu()));
connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
actionCollection()->addAssociatedWidget(this);
QMetaObject::invokeMethod(this, "initializeGeometry", Qt::QueuedConnection);
KoToolBoxFactory toolBoxFactory;
QDockWidget *toolbox = createDockWidget(&toolBoxFactory);
toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
if (cfg.toolOptionsInDocker()) {
ToolDockerFactory toolDockerFactory;
d->toolOptionsDocker = qobject_cast<KoToolDocker*>(createDockWidget(&toolDockerFactory));
}
QMap<QString, QAction*> dockwidgetActions;
dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
foreach(const QString & docker, KoDockRegistry::instance()->keys()) {
KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
QDockWidget *dw = createDockWidget(factory);
dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
}
foreach(QString title, dockwidgetActions.keys()) {
d->dockWidgetMenu->addAction(dockwidgetActions[title]);
}
foreach (QDockWidget *wdg, dockWidgets()) {
if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
wdg->setVisible(true);
}
}
foreach(KoCanvasObserverBase* observer, canvasObservers()) {
observer->setObservedCanvas(0);
KisMainwindowObserver* mainwindowObserver = dynamic_cast<KisMainwindowObserver*>(observer);
if (mainwindowObserver) {
mainwindowObserver->setMainWindow(d->viewManager);
}
}
d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setTabPosition(QTabWidget::North);
d->mdiArea->setTabsClosable(true);
setCentralWidget(d->mdiArea);
d->mdiArea->show();
connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated()));
connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));
connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*)));
createActions();
- setAutoSaveSettings(KisFactory::componentName(), false);
+ setAutoSaveSettings("krita", false);
KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), viewManager());
subWindowActivated();
updateWindowMenu();
if (isHelpMenuEnabled() && !d->helpMenu) {
d->helpMenu = new KHelpMenu( this, *KisFactory::aboutData(), false );
KActionCollection *actions = actionCollection();
QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents);
QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis);
QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug);
QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp);
QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE);
if (helpContentsAction) {
actions->addAction(helpContentsAction->objectName(), helpContentsAction);
}
if (whatsThisAction) {
actions->addAction(whatsThisAction->objectName(), whatsThisAction);
}
if (reportBugAction) {
actions->addAction(reportBugAction->objectName(), reportBugAction);
}
if (switchLanguageAction) {
actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
}
if (aboutAppAction) {
actions->addAction(aboutAppAction->objectName(), aboutAppAction);
}
if (aboutKdeAction) {
actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
}
connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication()));
}
// KDE libss 4' help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves
QAction *helpAction = actionCollection()->action("help_contents");
helpAction->disconnect();
connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual()));
#if 0
//check for colliding shortcuts
QSet<QKeySequence> existingShortcuts;
foreach(QAction* action, actionCollection()->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
- qDebug() << "shortcut " << action->text() << " " << action->shortcut();
+ dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
existingShortcuts.insert(action->shortcut());
}
#endif
configChanged();
QString doc;
QStringList allFiles = KGlobal::dirs()->findAllResources("data", "krita/krita.rc");
KIS_ASSERT(allFiles.size() > 0); // We need at least one krita.rc file!
setXMLFile(findMostRecentXMLFile(allFiles, doc));
setLocalXMLFile(KStandardDirs::locateLocal("data", "krita/krita.rc"));
guiFactory()->addClient(this);
// Create and plug toolbar list for Settings menu
QList<QAction *> toolbarList;
foreach(QWidget* it, guiFactory()->containers("ToolBar")) {
KToolBar * toolBar = ::qobject_cast<KToolBar *>(it);
if (toolBar) {
if (toolBar->objectName() == "BrushesAndStuff") {
toolBar->setEnabled(false);
}
KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
act->setChecked(!toolBar->isHidden());
toolbarList.append(act);
} else
- kWarning(30003) << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
+ warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
}
plugActionList("toolbarlist", toolbarList);
setToolbarList(toolbarList);
applyToolBarLayout();
d->viewManager->updateGUI();
d->viewManager->updateIcons();
QTimer::singleShot(1000, this, SLOT(checkSanity()));
}
void KisMainWindow::setNoCleanup(bool noCleanup)
{
d->noCleanup = noCleanup;
}
KisMainWindow::~KisMainWindow()
{
// foreach(QAction *ac, actionCollection()->actions()) {
// KAction *action = qobject_cast<KAction*>(ac);
// if (action) {
-// qDebug() << "<Action"
+// dbgKrita << "<Action"
// << "name=" << action->objectName()
// << "icon=" << action->icon().name()
// << "text=" << action->text().replace("&", "&amp;")
// << "whatsThis=" << action->whatsThis()
// << "toolTip=" << action->toolTip().replace("<html>", "").replace("</html>", "")
// << "iconText=" << action->iconText().replace("&", "&amp;")
// << "shortcut=" << action->shortcut(KAction::ActiveShortcut).toString()
// << "defaultShortcut=" << action->shortcut(KAction::DefaultShortcut).toString()
// << "isCheckable=" << QString((action->isChecked() ? "true" : "false"))
// << "statusTip=" << action->statusTip()
// << "/>" ;
// }
// else {
-// qDebug() << "Got a QAction:" << ac->objectName();
+// dbgKrita << "Got a QAction:" << ac->objectName();
// }
// }
KConfigGroup cfg(KGlobal::config(), "MainWindow");
cfg.writeEntry("ko_geometry", saveGeometry().toBase64());
cfg.writeEntry("ko_windowstate", saveState().toBase64());
{
KConfigGroup group(KGlobal::config(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
}
// The doc and view might still exist (this is the case when closing the window)
KisPart::instance()->removeMainWindow(this);
if (d->noCleanup)
return;
delete d->viewManager;
delete d;
}
void KisMainWindow::addView(KisView *view)
{
- //qDebug() << "KisMainWindow::addView" << view;
+ //dbgKrita << "KisMainWindow::addView" << view;
if (d->activeView == view) return;
if (d->activeView) {
d->activeView->disconnect(this);
}
showView(view);
updateCaption();
emit restoringDone();
if (d->activeView) {
connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified(QString,bool)));
}
}
void KisMainWindow::showView(KisView *imageView)
{
if (imageView && activeView() != imageView) {
// XXX: find a better way to initialize this!
imageView->setViewManager(d->viewManager);
imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager());
imageView->slotLoadingFinished();
QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView);
subwin->setAttribute(Qt::WA_DeleteOnClose, true);
connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu()));
KisConfig cfg;
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setWindowIcon(qApp->windowIcon());
if (d->mdiArea->subWindowList().size() == 1) {
imageView->showMaximized();
}
else {
imageView->show();
}
setActiveView(imageView);
updateWindowMenu();
updateCaption();
}
}
void KisMainWindow::slotPreferences()
{
if (KisDlgPreferences::editPreferences()) {
KisConfigNotifier::instance()->notifyConfigChanged();
// XXX: should this be changed for the views in other windows as well?
foreach(QPointer<KisView> koview, KisPart::instance()->views()) {
KisViewManager *view = qobject_cast<KisViewManager*>(koview);
if (view) {
// Update the settings for all nodes -- they don't query
// KisConfig directly because they need the settings during
// compositing, and they don't connect to the config notifier
// because nodes are not QObjects (because only one base class
// can be a QObject).
KisNode* node = dynamic_cast<KisNode*>(view->image()->rootLayer().data());
node->updateSettings();
}
}
d->viewManager->showHideScrollbars();
}
}
void KisMainWindow::slotThemeChanged()
{
// save theme changes instantly
KConfigGroup group(KGlobal::config(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
// reload action icons!
foreach (QAction *action, actionCollection()->actions()) {
KisIconUtils::updateIcon(action);
}
emit themeChanged();
}
void KisMainWindow::updateReloadFileAction(KisDocument *doc)
{
Q_UNUSED(doc);
// d->reloadFile->setEnabled(doc && !doc->url().isEmpty());
}
void KisMainWindow::setReadWrite(bool readwrite)
{
d->saveAction->setEnabled(readwrite);
d->importFile->setEnabled(readwrite);
d->readOnly = !readwrite;
updateCaption();
}
void KisMainWindow::addRecentURL(const KUrl& url)
{
- kDebug(30003) << "KisMainWindow::addRecentURL url=" << url.prettyUrl();
+ dbgUI << "KisMainWindow::addRecentURL url=" << url.prettyUrl();
// Add entry to recent documents list
// (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.)
if (!url.isEmpty()) {
bool ok = true;
if (url.isLocalFile()) {
QString path = url.toLocalFile(KUrl::RemoveTrailingSlash);
const QStringList tmpDirs = KGlobal::dirs()->resourceDirs("tmp");
for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it)
if (path.contains(*it))
ok = false; // it's in the tmp resource
if (ok) {
KRecentDocument::add(path);
KRecentDirs::add(":OpenDialog", QFileInfo(path).dir().canonicalPath());
}
} else {
KRecentDocument::add(url.url(KUrl::RemoveTrailingSlash), true);
}
if (ok) {
d->recentFiles->addUrl(url);
}
saveRecentFiles();
}
}
void KisMainWindow::saveRecentFiles()
{
// Save list of recent files
KSharedConfigPtr config = KisFactory::componentData().config();
d->recentFiles->saveEntries(config->group("RecentFiles"));
config->sync();
// Tell all windows to reload their list, after saving
// Doesn't work multi-process, but it's a start
foreach(KMainWindow* window, KMainWindow::memberList())
static_cast<KisMainWindow *>(window)->reloadRecentFileList();
}
void KisMainWindow::reloadRecentFileList()
{
KSharedConfigPtr config = KisFactory::componentData().config();
d->recentFiles->loadEntries(config->group("RecentFiles"));
}
void KisMainWindow::updateCaption()
{
if (!d->mdiArea->activeSubWindow()) {
updateCaption(QString(), false);
}
else {
QString caption( d->activeView->document()->caption() );
if (d->readOnly) {
caption += ' ' + i18n("(write protected)");
}
d->activeView->setWindowTitle(caption);
updateCaption(caption, d->activeView->document()->isModified());
if (!d->activeView->document()->url().fileName(KUrl::ObeyTrailingSlash).isEmpty())
d->saveAction->setToolTip(i18n("Save as %1", d->activeView->document()->url().fileName(KUrl::ObeyTrailingSlash)));
else
d->saveAction->setToolTip(i18n("Save"));
}
}
void KisMainWindow::updateCaption(const QString & caption, bool mod)
{
- kDebug(30003) << "KisMainWindow::updateCaption(" << caption << "," << mod << ")";
+ dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")";
#ifdef CALLIGRA_ALPHA
setCaption(QString("ALPHA %1: %2").arg(CALLIGRA_ALPHA).arg(caption), mod);
return;
#endif
#ifdef CALLIGRA_BETA
setCaption(QString("BETA %1: %2").arg(CALLIGRA_BETA).arg(caption), mod);
return;
#endif
#ifdef CALLIGRA_RC
setCaption(QString("RELEASE CANDIDATE %1: %2").arg(CALLIGRA_RC).arg(caption), mod);
return;
#endif
setCaption(caption, mod);
}
KisView *KisMainWindow::activeView() const
{
if (d->activeView) {
return d->activeView;
}
return 0;
}
bool KisMainWindow::openDocument(const KUrl & url)
{
if (!QFile(url.toLocalFile()).exists()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url()));
d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list
saveRecentFiles();
return false;
}
return openDocumentInternal(url);
}
bool KisMainWindow::openDocumentInternal(const KUrl & url, KisDocument *newdoc)
{
if (!newdoc) {
newdoc = KisPart::instance()->createDocument();
}
d->firstTime = true;
connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &)));
bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url);
if (!openRet) {
delete newdoc;
return false;
}
KisPart::instance()->addDocument(newdoc);
updateReloadFileAction(newdoc);
KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown);
if (!file.isWritable()) {
setReadWrite(false);
}
return true;
}
// Separate from openDocument to handle async loading (remote URLs)
void KisMainWindow::slotLoadCompleted()
{
KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
KisView *view = KisPart::instance()->createView(newdoc, resourceManager(), actionCollection(), this);
addView(view);
disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &)));
emit loadCompleted();
}
void KisMainWindow::slotLoadCanceled(const QString & errMsg)
{
- kDebug(30003) << "KisMainWindow::slotLoadCanceled";
+ dbgUI << "KisMainWindow::slotLoadCanceled";
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
// ... can't delete the document, it's the one who emitted the signal...
KisDocument* doc = qobject_cast<KisDocument*>(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &)));
}
void KisMainWindow::slotSaveCanceled(const QString &errMsg)
{
- kDebug(30003) << "KisMainWindow::slotSaveCanceled";
+ dbgUI << "KisMainWindow::slotSaveCanceled";
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
slotSaveCompleted();
}
void KisMainWindow::slotSaveCompleted()
{
- kDebug(30003) << "KisMainWindow::slotSaveCompleted";
+ dbgUI << "KisMainWindow::slotSaveCompleted";
KisDocument* doc = qobject_cast<KisDocument*>(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &)));
if (d->deferredClosingEvent) {
KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
}
}
bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool silent, int specialOutputFlag)
{
if (!document) {
return true;
}
bool reset_url;
if (document->url().isEmpty()) {
reset_url = true;
saveas = true;
} else {
reset_url = false;
}
connect(document, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int)));
connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &)));
KUrl oldURL = document->url();
QString oldFile = document->localFilePath();
QByteArray _native_format = document->nativeFormatMimeType();
QByteArray oldOutputFormat = document->outputMimeType();
int oldSpecialOutputFlag = document->specialOutputFlag();
KUrl suggestedURL = document->url();
QStringList mimeFilter;
KMimeType::Ptr mime = KMimeType::mimeType(_native_format);
if (! mime)
mime = KMimeType::defaultMimeTypePtr();
if (specialOutputFlag)
mimeFilter = mime->patterns();
else
mimeFilter = KisImportExportManager::mimeFilter(_native_format,
KisImportExportManager::Export,
document->extraNativeMimeTypes());
if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) {
- kDebug(30003) << "KisMainWindow::saveDocument no export filter for" << oldOutputFormat;
+ dbgUI << "KisMainWindow::saveDocument no export filter for" << oldOutputFormat;
// --- don't setOutputMimeType in case the user cancels the Save As
// dialog and then tries to just plain Save ---
// suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
QString suggestedFilename = suggestedURL.fileName();
if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name
int c = suggestedFilename.lastIndexOf('.');
const QString ext = mime->mainExtension();
if (!ext.isEmpty()) {
if (c < 0)
suggestedFilename += ext;
else
suggestedFilename = suggestedFilename.left(c) + ext;
} else { // current filename extension wrong anyway
if (c > 0) {
// this assumes that a . signifies an extension, not just a .
suggestedFilename = suggestedFilename.left(c);
}
}
suggestedURL.setFileName(suggestedFilename);
}
// force the user to choose outputMimeType
saveas = true;
}
bool ret = false;
if (document->url().isEmpty() || saveas) {
// if you're just File/Save As'ing to change filter options you
// don't want to be reminded about overwriting files etc.
bool justChangingFilterOptions = false;
KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument");
dialog.setCaption(i18n("untitled"));
if (isExporting() && !d->lastExportUrl.isEmpty()) {
dialog.setDefaultDir(d->lastExportUrl.toLocalFile(), true);
}
else {
dialog.setDefaultDir(suggestedURL.toLocalFile(), true);
}
dialog.setMimeTypeFilters(mimeFilter, KIS_MIME_TYPE);
KUrl newURL = dialog.url();
if (newURL.isLocalFile()) {
QString fn = newURL.toLocalFile();
if (QFileInfo(fn).completeSuffix().isEmpty()) {
KMimeType::Ptr mime = KMimeType::mimeType(_native_format);
fn.append(mime->mainExtension());
newURL = KUrl::fromPath(fn);
}
}
if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
QString fn = newURL.toLocalFile();
QFileInfo info(fn);
document->documentInfo()->setAboutInfo("title", info.baseName());
}
QByteArray outputFormat = _native_format;
if (!specialOutputFlag) {
KMimeType::Ptr mime = KMimeType::findByUrl(newURL);
QString outputFormatString = mime->name();
outputFormat = outputFormatString.toLatin1();
}
if (!isExporting())
justChangingFilterOptions = (newURL == document->url()) &&
(outputFormat == document->mimeType()) &&
(specialOutputFlag == oldSpecialOutputFlag);
else
justChangingFilterOptions = (newURL == d->lastExportUrl) &&
(outputFormat == d->lastExportedFormat) &&
(specialOutputFlag == d->lastExportSpecialOutputFlag);
bool bOk = true;
if (newURL.isEmpty()) {
bOk = false;
}
// adjust URL before doing checks on whether the file exists.
if (specialOutputFlag) {
QString fileName = newURL.fileName();
if ( specialOutputFlag== KisDocument::SaveAsDirectoryStore) {
- //qDebug() << "save to directory: " << newURL.url();
+ //dbgKrita << "save to directory: " << newURL.url();
}
else if (specialOutputFlag == KisDocument::SaveEncrypted) {
int dot = fileName.lastIndexOf('.');
- qDebug() << dot;
+ dbgKrita << dot;
QString ext = mime->mainExtension();
if (!ext.isEmpty()) {
if (dot < 0) fileName += ext;
else fileName = fileName.left(dot) + ext;
} else { // current filename extension wrong anyway
if (dot > 0) fileName = fileName.left(dot);
}
newURL.setFileName(fileName);
}
}
if (bOk) {
bool wantToSave = true;
// don't change this line unless you know what you're doing :)
if (!justChangingFilterOptions || document->confirmNonNativeSave(isExporting())) {
if (!document->isNativeFormat(outputFormat))
wantToSave = true;
}
if (wantToSave) {
//
// Note:
// If the user is stupid enough to Export to the current URL,
// we do _not_ change this operation into a Save As. Reasons
// follow:
//
// 1. A check like "isExporting() && oldURL == newURL"
// doesn't _always_ work on case-insensitive filesystems
// and inconsistent behaviour is bad.
// 2. It is probably not a good idea to change document->mimeType
// and friends because the next time the user File/Save's,
// (not Save As) they won't be expecting that they are
// using their File/Export settings
//
// As a bad side-effect of this, the modified flag will not
// be updated and it is possible that what is currently on
// their screen is not what is stored on disk (through loss
// of formatting). But if you are dumb enough to change
// mimetype but not the filename, then arguably, _you_ are
// the "bug" :)
//
// - Clarence
//
document->setOutputMimeType(outputFormat, specialOutputFlag);
if (!isExporting()) { // Save As
ret = document->saveAs(newURL);
if (ret) {
- kDebug(30003) << "Successful Save As!";
+ dbgUI << "Successful Save As!";
addRecentURL(newURL);
setReadWrite(true);
} else {
- kDebug(30003) << "Failed Save As!";
+ dbgUI << "Failed Save As!";
document->setUrl(oldURL);
document->setLocalFilePath(oldFile);
document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag);
}
} else { // Export
ret = document->exportDocument(newURL);
if (ret) {
// a few file dialog convenience things
d->lastExportUrl = newURL;
d->lastExportedFormat = outputFormat;
d->lastExportSpecialOutputFlag = specialOutputFlag;
}
// always restore output format
document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag);
}
if (silent) // don't let the document change the window caption
document->setTitleModified();
} // if (wantToSave) {
else
ret = false;
} // if (bOk) {
else
ret = false;
} else { // saving
bool needConfirm = document->confirmNonNativeSave(false) && !document->isNativeFormat(oldOutputFormat);
if (!needConfirm ||
(needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */))
) {
// be sure document has the correct outputMimeType!
if (isExporting() || document->isModified()) {
ret = document->save();
}
if (!ret) {
- kDebug(30003) << "Failed Save!";
+ dbgUI << "Failed Save!";
document->setUrl(oldURL);
document->setLocalFilePath(oldFile);
}
} else
ret = false;
}
if (!ret && reset_url)
document->resetURL(); //clean the suggested filename as the save dialog was rejected
updateReloadFileAction(document);
updateCaption();
return ret;
}
bool KisMainWindow::exportConfirmation(const QByteArray &/*outputFormat*/)
{
return true;
}
void KisMainWindow::undo()
{
if (activeView()) {
activeView()->undoAction()->trigger();
d->undo->setText(activeView()->undoAction()->text());
}
}
void KisMainWindow::redo()
{
if (activeView()) {
activeView()->redoAction()->trigger();
d->redo->setText(activeView()->redoAction()->text());
}
}
void KisMainWindow::closeEvent(QCloseEvent *e)
{
d->mdiArea->closeAllSubWindows();
if(d->activeView && d->activeView->document() && d->activeView->document()->isLoading()) {
e->setAccepted(false);
return;
}
QList<QMdiSubWindow*> childrenList = d->mdiArea->subWindowList();
if (childrenList.isEmpty()) {
d->deferredClosingEvent = e;
if (!d->dockerStateBeforeHiding.isEmpty()) {
restoreState(d->dockerStateBeforeHiding);
}
statusBar()->setVisible(true);
menuBar()->setVisible(true);
saveWindowSettings();
if (d->noCleanup)
return;
foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
KisView *view = dynamic_cast<KisView*>(subwin);
if (view) {
KisPart::instance()->removeView(view);
}
}
if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency
foreach(QDockWidget* dockWidget, d->dockWidgetsMap)
dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget));
}
} else {
e->setAccepted(false);
}
}
void KisMainWindow::saveWindowSettings()
{
KSharedConfigPtr config = KisFactory::componentData().config();
if (d->windowSizeDirty ) {
// Save window size into the config file of our componentData
- kDebug(30003) << "KisMainWindow::saveWindowSettings";
+ dbgUI << "KisMainWindow::saveWindowSettings";
KConfigGroup group = config->group("MainWindow");
saveWindowSize(group);
config->sync();
d->windowSizeDirty = false;
}
if (!d->activeView || d->activeView->document()) {
// Save toolbar position into the config file of the app, under the doc's component name
- KConfigGroup group = KGlobal::config()->group(KisFactory::componentName());
+ KConfigGroup group = KGlobal::config()->group("krita");
saveMainWindowSettings(group);
// Save collapsable state of dock widgets
for (QMap<QString, QDockWidget*>::const_iterator i = d->dockWidgetsMap.constBegin();
i != d->dockWidgetsMap.constEnd(); ++i) {
if (i.value()->widget()) {
KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden());
dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
}
}
}
KGlobal::config()->sync();
resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down
}
void KisMainWindow::resizeEvent(QResizeEvent * e)
{
d->windowSizeDirty = true;
KXmlGuiWindow::resizeEvent(e);
}
void KisMainWindow::setActiveView(KisView* view)
{
d->activeView = view;
updateCaption();
actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text());
actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text());
}
void KisMainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasUrls()) {
event->accept();
}
}
void KisMainWindow::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
foreach(const QUrl &url, event->mimeData()->urls()) {
openDocument(url);
}
}
}
void KisMainWindow::slotFileNew()
{
KisPart::instance()->showStartUpWidget(this, true /*Always show widget*/);
}
void KisMainWindow::slotFileOpen()
{
QStringList urls;
KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
- dialog.setMimeTypeFilters(koApp->mimeFilter(KisImportExportManager::Import));
+ dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KIS_MIME_TYPE,
+ KisImportExportManager::Import,
+ KisDocumentEntry::extraNativeMimeTypes()));
QStringList filters = dialog.nameFilters();
filters << i18n("All files (*.*)");
dialog.setNameFilters(filters);
dialog.setHideNameFilterDetailsOption();
dialog.setCaption(isImporting() ? i18n("Import Images") : i18n("Open Images"));
urls = dialog.urls();
if (urls.isEmpty())
return;
foreach(const QString& url, urls) {
if (!url.isEmpty()) {
bool res = openDocument(KUrl::fromLocalFile(url));
if (!res) {
- qWarning() << "Loading" << url << "failed";
+ warnKrita << "Loading" << url << "failed";
}
}
}
}
void KisMainWindow::slotFileOpenRecent(const QUrl & url)
{
// Create a copy, because the original KUrl in the map of recent files in
// KRecentFilesAction may get deleted.
(void) openDocument(KUrl(url));
}
void KisMainWindow::slotFileSave()
{
if (saveDocument(d->activeView->document()))
emit documentSaved();
}
void KisMainWindow::slotFileSaveAs()
{
if (saveDocument(d->activeView->document(), true))
emit documentSaved();
}
KoCanvasResourceManager *KisMainWindow::resourceManager() const
{
return d->viewManager->resourceProvider()->resourceManager();
}
int KisMainWindow::viewCount() const
{
return d->mdiArea->subWindowList().size();
}
bool KisMainWindow::restoreWorkspace(const QByteArray &state)
{
QByteArray oldState = saveState();
const bool showTitlebars = KisConfig().showDockerTitleBars();
// needed because otherwise the layout isn't correctly restored in some situations
Q_FOREACH (QDockWidget *dock, dockWidgets()) {
dock->hide();
dock->titleBarWidget()->setVisible(showTitlebars);
}
bool success = KXmlGuiWindow::restoreState(state);
if (!success) {
KXmlGuiWindow::restoreState(oldState);
Q_FOREACH (QDockWidget *dock, dockWidgets()) {
if (dock->titleBarWidget()) {
dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating());
}
}
return false;
}
Q_FOREACH (QDockWidget *dock, dockWidgets()) {
if (dock->titleBarWidget()) {
const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget();
dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed));
}
}
return success;
}
KisViewManager *KisMainWindow::viewManager() const
{
return d->viewManager;
}
void KisMainWindow::slotDocumentInfo()
{
if (!d->activeView->document())
return;
KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
if (!docInfo)
return;
KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
if (dlg->exec()) {
if (dlg->isDocumentSaved()) {
d->activeView->document()->setModified(false);
} else {
d->activeView->document()->setModified(true);
}
d->activeView->document()->setTitleModified();
}
delete dlg;
}
bool KisMainWindow::slotFileCloseAll()
{
foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (subwin) {
if(!subwin->close())
return false;
}
}
updateCaption();
return true;
}
void KisMainWindow::slotFileQuit()
{
if(!slotFileCloseAll())
return;
close();
foreach(QPointer<KisMainWindow> mainWin, KisPart::instance()->mainWindows()) {
if (mainWin != this) {
if(!mainWin->slotFileCloseAll())
return;
mainWin->close();
}
}
}
void KisMainWindow::slotFilePrint()
{
if (!activeView())
return;
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return;
applyDefaultSettings(printJob->printer());
QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this );
if (printDialog && printDialog->exec() == QDialog::Accepted) {
printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point);
printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()),
activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())),
QPrinter::Inch);
printJob->startPrinting(KisPrintJob::DeleteWhenDone);
}
else {
delete printJob;
}
delete printDialog;
}
void KisMainWindow::slotFilePrintPreview()
{
if (!activeView())
return;
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return;
/* Sets the startPrinting() slot to be blocking.
The Qt print-preview dialog requires the printing to be completely blocking
and only return when the full document has been printed.
By default the KisPrintingDialog is non-blocking and
multithreading, setting blocking to true will allow it to be used in the preview dialog */
printJob->setProperty("blocking", true);
QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this);
printJob->setParent(preview); // will take care of deleting the job
connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting()));
preview->exec();
delete preview;
}
KisPrintJob* KisMainWindow::exportToPdf(const QString &pdfFileName)
{
if (!activeView())
return 0;
KoPageLayout pageLayout;
pageLayout = activeView()->pageLayout();
return exportToPdf(pageLayout, pdfFileName);
}
KisPrintJob* KisMainWindow::exportToPdf(KoPageLayout pageLayout, QString pdfFileName)
{
if (!activeView())
return 0;
if (!activeView()->document())
return 0;
if (pdfFileName.isEmpty()) {
KConfigGroup group = KGlobal::config()->group("File Dialogs");
QString defaultDir = group.readEntry("SavePdfDialog");
if (defaultDir.isEmpty())
defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
KUrl startUrl = KUrl(defaultDir);
KisDocument* pDoc = d->activeView->document();
/** if document has a file name, take file name and replace extension with .pdf */
if (pDoc && pDoc->url().isValid()) {
startUrl = pDoc->url();
QString fileName = startUrl.fileName();
fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" );
startUrl.setFileName( fileName );
}
QPointer<KoPageLayoutDialog> layoutDlg(new KoPageLayoutDialog(this, pageLayout));
layoutDlg->setWindowModality(Qt::WindowModal);
if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) {
delete layoutDlg;
return 0;
}
pageLayout = layoutDlg->pageLayout();
delete layoutDlg;
KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument");
dialog.setCaption(i18n("Export as PDF"));
dialog.setDefaultDir(startUrl.toLocalFile());
dialog.setMimeTypeFilters(QStringList() << "application/pdf");
KUrl url = dialog.url();
pdfFileName = url.toLocalFile();
if (pdfFileName.isEmpty())
return 0;
}
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return 0;
if (isHidden()) {
printJob->setProperty("noprogressdialog", true);
}
applyDefaultSettings(printJob->printer());
// TODO for remote files we have to first save locally and then upload.
printJob->printer().setOutputFileName(pdfFileName);
printJob->printer().setDocName(pdfFileName);
printJob->printer().setColorMode(QPrinter::Color);
if (pageLayout.format == KoPageFormat::CustomSize) {
printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter);
} else {
printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format));
}
printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter);
switch (pageLayout.orientation) {
case KoPageFormat::Portrait:
printJob->printer().setOrientation(QPrinter::Portrait);
break;
case KoPageFormat::Landscape:
printJob->printer().setOrientation(QPrinter::Landscape);
break;
}
//before printing check if the printer can handle printing
if (!printJob->canPrint()) {
QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file"));
}
printJob->startPrinting(KisPrintJob::DeleteWhenDone);
return printJob;
}
void KisMainWindow::slotConfigureKeys()
{
KisPart::instance()->configureShortcuts();
emit keyBindingsChanged();
}
void KisMainWindow::slotConfigureToolbars()
{
- KConfigGroup group = KGlobal::config()->group(KisFactory::componentName());
+ KConfigGroup group = KGlobal::config()->group("krita");
saveMainWindowSettings(group);
KEditToolBar edit(factory(), this);
connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
(void) edit.exec();
applyToolBarLayout();
}
void KisMainWindow::slotNewToolbarConfig()
{
- applyMainWindowSettings(KGlobal::config()->group(KisFactory::componentName()));
+ applyMainWindowSettings(KGlobal::config()->group("krita"));
KXMLGUIFactory *factory = guiFactory();
Q_UNUSED(factory);
// Check if there's an active view
if (!d->activeView)
return;
plugActionList("toolbarlist", d->toolbarList);
applyToolBarLayout();
}
void KisMainWindow::slotToolbarToggled(bool toggle)
{
- //kDebug(30003) <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
+ //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
// The action (sender) and the toolbar have the same name
KToolBar * bar = toolBar(sender()->objectName());
if (bar) {
if (toggle) {
bar->show();
}
else {
bar->hide();
}
if (d->activeView && d->activeView->document()) {
- KConfigGroup group = KGlobal::config()->group(KisFactory::componentName());
+ KConfigGroup group = KGlobal::config()->group("krita");
saveMainWindowSettings(group);
}
} else
- kWarning(30003) << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
+ warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
}
void KisMainWindow::viewFullscreen(bool fullScreen)
{
KisConfig cfg;
cfg.setFullscreenMode(fullScreen);
if (fullScreen) {
setWindowState(windowState() | Qt::WindowFullScreen); // set
} else {
setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
}
}
void KisMainWindow::slotProgress(int value)
{
qApp->processEvents();
if (!d->progressMutex.tryLock()) return;
- kDebug(30003) << "KisMainWindow::slotProgress" << value;
+ dbgUI << "KisMainWindow::slotProgress" << value;
if (value <= -1 || value >= 100) {
if (d->progress) {
statusBar()->removeWidget(d->progress);
delete d->progress;
d->progress = 0;
}
d->firstTime = true;
d->progressMutex.unlock();
return;
}
if (d->firstTime || !d->progress) {
// The statusbar might not even be created yet.
// So check for that first, and create it if necessary
QStatusBar *bar = findChild<QStatusBar *>();
if (!bar) {
statusBar()->show();
QApplication::sendPostedEvents(this, QEvent::ChildAdded);
}
if (d->progress) {
statusBar()->removeWidget(d->progress);
delete d->progress;
d->progress = 0;
}
d->progress = new QProgressBar(statusBar());
d->progress->setMaximumHeight(statusBar()->fontMetrics().height());
d->progress->setRange(0, 100);
statusBar()->addPermanentWidget(d->progress);
d->progress->show();
d->firstTime = false;
}
if (!d->progress.isNull()) {
d->progress->setValue(value);
}
qApp->processEvents();
d->progressMutex.unlock();
}
void KisMainWindow::setMaxRecentItems(uint _number)
{
d->recentFiles->setMaxItems(_number);
}
void KisMainWindow::slotReloadFile()
{
KisDocument* document = d->activeView->document();
if (!document || document->url().isEmpty())
return;
if (document->isModified()) {
bool ok = QMessageBox::question(this,
i18nc("@title:window", "Krita"),
i18n("You will lose all changes made since your last save\n"
"Do you want to continue?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes;
if (!ok)
return;
}
KUrl url = document->url();
saveWindowSettings();
if (!document->reload()) {
QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document"));
}
return;
}
void KisMainWindow::slotImportFile()
{
- kDebug(30003) << "slotImportFile()";
+ dbgUI << "slotImportFile()";
d->isImporting = true;
slotFileOpen();
d->isImporting = false;
}
void KisMainWindow::slotExportFile()
{
- kDebug(30003) << "slotExportFile()";
+ dbgUI << "slotExportFile()";
d->isExporting = true;
slotFileSaveAs();
d->isExporting = false;
}
bool KisMainWindow::isImporting() const
{
return d->isImporting;
}
bool KisMainWindow::isExporting() const
{
return d->isExporting;
}
QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
{
QDockWidget* dockWidget = 0;
if (!d->dockWidgetsMap.contains(factory->id())) {
dockWidget = factory->createDockWidget();
// It is quite possible that a dock factory cannot create the dock; don't
// do anything in that case.
if (!dockWidget) {
- qWarning() << "Could not create docker for" << factory->id();
+ warnKrita << "Could not create docker for" << factory->id();
return 0;
}
KoDockWidgetTitleBar *titleBar = dynamic_cast<KoDockWidgetTitleBar*>(dockWidget->titleBarWidget());
// Check if the dock widget is supposed to be collapsable
if (!dockWidget->titleBarWidget()) {
titleBar = new KoDockWidgetTitleBar(dockWidget);
dockWidget->setTitleBarWidget(titleBar);
titleBar->setCollapsable(factory->isCollapsable());
}
dockWidget->setObjectName(factory->id());
dockWidget->setParent(this);
if (dockWidget->widget() && dockWidget->widget()->layout())
dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
bool visible = true;
switch (factory->defaultDockPosition()) {
case KoDockFactoryBase::DockTornOff:
dockWidget->setFloating(true); // position nicely?
break;
case KoDockFactoryBase::DockTop:
side = Qt::TopDockWidgetArea; break;
case KoDockFactoryBase::DockLeft:
side = Qt::LeftDockWidgetArea; break;
case KoDockFactoryBase::DockBottom:
side = Qt::BottomDockWidgetArea; break;
case KoDockFactoryBase::DockRight:
side = Qt::RightDockWidgetArea; break;
case KoDockFactoryBase::DockMinimized:
default:
side = Qt::RightDockWidgetArea;
visible = false;
}
- KConfigGroup group = KGlobal::config()->group(KisFactory::componentName()).group("DockWidget " + factory->id());
+ KConfigGroup group = KGlobal::config()->group("krita").group("DockWidget " + factory->id());
side = static_cast<Qt::DockWidgetArea>(group.readEntry("DockArea", static_cast<int>(side)));
if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
addDockWidget(side, dockWidget);
if (!visible) {
dockWidget->hide();
}
bool collapsed = factory->defaultCollapsed();
bool locked = false;
- group = KGlobal::config()->group(KisFactory::componentName()).group("DockWidget " + factory->id());
+ group = KGlobal::config()->group("krita").group("DockWidget " + factory->id());
collapsed = group.readEntry("Collapsed", collapsed);
locked = group.readEntry("Locked", locked);
- //qDebug() << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar;
+ //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar;
if (titleBar && collapsed)
titleBar->setCollapsed(true);
if (titleBar && locked)
titleBar->setLocked(true);
d->dockWidgetsMap.insert(factory->id(), dockWidget);
} else {
dockWidget = d->dockWidgetsMap[factory->id()];
}
KConfigGroup group(KGlobal::config(), "GUI");
QFont dockWidgetFont = KGlobalSettings::generalFont();
qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75);
dockWidgetFont.setPointSizeF(qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF()));
#ifdef Q_OS_MAC
dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
#endif
dockWidget->setFont(dockWidgetFont);
connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
return dockWidget;
}
void KisMainWindow::forceDockTabFonts()
{
foreach(QObject *child, children()) {
if (child->inherits("QTabBar")) {
KConfigGroup group(KGlobal::config(), "GUI");
QFont dockWidgetFont = KGlobalSettings::generalFont();
qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75);
dockWidgetFont.setPointSizeF(qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF()));
((QTabBar *)child)->setFont(dockWidgetFont);
}
}
}
QList<QDockWidget*> KisMainWindow::dockWidgets() const
{
return d->dockWidgetsMap.values();
}
QList<KoCanvasObserverBase*> KisMainWindow::canvasObservers() const
{
QList<KoCanvasObserverBase*> observers;
foreach(QDockWidget *docker, dockWidgets()) {
KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
if (observer) {
observers << observer;
}
else {
- qWarning() << docker << "is not a canvas observer";
+ warnKrita << docker << "is not a canvas observer";
}
}
return observers;
}
void KisMainWindow::toggleDockersVisibility(bool visible)
{
if (!visible) {
d->dockerStateBeforeHiding = saveState();
foreach(QObject* widget, children()) {
if (widget->inherits("QDockWidget")) {
QDockWidget* dw = static_cast<QDockWidget*>(widget);
if (dw->isVisible()) {
dw->hide();
}
}
}
}
else {
restoreState(d->dockerStateBeforeHiding);
}
}
void KisMainWindow::setToolbarList(QList<QAction *> toolbarList)
{
qDeleteAll(d->toolbarList);
d->toolbarList = toolbarList;
}
void KisMainWindow::slotDocumentTitleModified(const QString &caption, bool mod)
{
updateCaption(caption, mod);
updateReloadFileAction(d->activeView ? d->activeView->document() : 0);
}
void KisMainWindow::subWindowActivated()
{
bool enabled = (activeKisView() != 0);
d->mdiCascade->setEnabled(enabled);
d->mdiNextWindow->setEnabled(enabled);
d->mdiPreviousWindow->setEnabled(enabled);
d->mdiTile->setEnabled(enabled);
d->close->setEnabled(enabled);
d->closeAll->setEnabled(enabled);
setActiveSubWindow(d->mdiArea->activeSubWindow());
foreach(QToolBar *tb, toolBars()) {
if (tb->objectName() == "BrushesAndStuff") {
tb->setEnabled(enabled);
}
}
updateCaption();
d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::updateWindowMenu()
{
QMenu *menu = d->windowMenu->menu();
menu->clear();
menu->addAction(d->newWindow);
menu->addAction(d->documentMenu);
QMenu *docMenu = d->documentMenu->menu();
docMenu->clear();
foreach (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
if (doc) {
QString title = doc->url().prettyUrl();
if (title.isEmpty()) title = doc->image()->objectName();
QAction *action = docMenu->addAction(title);
action->setIcon(qApp->windowIcon());
connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
d->documentMapper->setMapping(action, doc);
}
}
menu->addSeparator();
menu->addAction(d->close);
menu->addAction(d->closeAll);
if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
menu->addSeparator();
menu->addAction(d->mdiTile);
menu->addAction(d->mdiCascade);
}
menu->addSeparator();
menu->addAction(d->mdiNextWindow);
menu->addAction(d->mdiPreviousWindow);
menu->addSeparator();
QList<QMdiSubWindow *> windows = d->mdiArea->subWindowList();
for (int i = 0; i < windows.size(); ++i) {
QPointer<KisView>child = qobject_cast<KisView*>(windows.at(i)->widget());
if (child) {
QString text;
if (i < 9) {
text = i18n("&%1 %2", i + 1, child->document()->url().prettyUrl());
}
else {
text = i18n("%1 %2", i + 1, child->document()->url().prettyUrl());
}
QAction *action = menu->addAction(text);
action->setIcon(qApp->windowIcon());
action->setCheckable(true);
action->setChecked(child == activeKisView());
connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
d->windowMapper->setMapping(action, windows.at(i));
}
}
updateCaption();
}
void KisMainWindow::setActiveSubWindow(QWidget *window)
{
if (!window) return;
QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
- //qDebug() << "setActiveSubWindow();" << subwin << d->activeSubWindow;
+ //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
if (subwin && subwin != d->activeSubWindow) {
KisView *view = qobject_cast<KisView *>(subwin->widget());
- //qDebug() << "\t" << view << activeView();
+ //dbgKrita << "\t" << view << activeView();
if (view && view != activeView()) {
d->viewManager->setCurrentView(view);
setActiveView(view);
}
d->activeSubWindow = subwin;
}
updateWindowMenu();
d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::configChanged()
{
KisConfig cfg;
QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
d->mdiArea->setViewMode(viewMode);
foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
}
KConfigGroup group(KGlobal::config(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
d->viewManager->actionManager()->updateGUI();
QBrush brush(cfg.getMDIBackgroundColor());
d->mdiArea->setBackground(brush);
QString backgroundImage = cfg.getMDIBackgroundImage();
if (backgroundImage != "") {
QImage image(backgroundImage);
QBrush brush(image);
d->mdiArea->setBackground(brush);
}
d->mdiArea->update();
}
void KisMainWindow::newView(QObject *document)
{
KisDocument *doc = qobject_cast<KisDocument*>(document);
KisView *view = KisPart::instance()->createView(doc, resourceManager(), actionCollection(), this);
addView(view);
d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::newWindow()
{
KisPart::instance()->createMainWindow()->show();
}
void KisMainWindow::closeCurrentWindow()
{
d->mdiArea->currentSubWindow()->close();
d->viewManager->actionManager()->updateGUI();
}
void KisMainWindow::checkSanity()
{
// print error if the lcms engine is not available
if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
// need to wait 1 event since exiting here would not work.
m_errorMessage = i18n("The Calligra LittleCMS color management plugin is not installed. Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
if (rserver->resources().isEmpty()) {
m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
}
void KisMainWindow::showErrorAndDie()
{
QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage);
if (m_dieOnError) {
exit(10);
}
}
void KisMainWindow::showAboutApplication()
{
KisAboutApplication dlg(KisFactory::aboutData(), this);
dlg.exec();
}
QPointer<KisView>KisMainWindow::activeKisView()
{
if (!d->mdiArea) return 0;
QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
- //qDebug() << "activeKisView" << activeSubWindow;
+ //dbgKrita << "activeKisView" << activeSubWindow;
if (!activeSubWindow) return 0;
return qobject_cast<KisView*>(activeSubWindow->widget());
}
void KisMainWindow::newOptionWidgets(const QList<QPointer<QWidget> > &optionWidgetList)
{
KConfigGroup group(KGlobal::config(), "GUI");
QFont dockWidgetFont = KGlobalSettings::generalFont();
qreal pointSize = group.readEntry("palettefontsize", dockWidgetFont.pointSize() * 0.75);
pointSize = qMax(pointSize, KGlobalSettings::smallestReadableFont().pointSizeF());
dockWidgetFont.setPointSizeF(pointSize);
foreach(QWidget *w, optionWidgetList) {
#ifdef Q_OS_MAC
w->setAttribute(Qt::WA_MacSmallSize, true);
#endif
w->setFont(dockWidgetFont);
}
if (d->toolOptionsDocker) {
d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
}
else {
d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
}
}
void KisMainWindow::applyDefaultSettings(QPrinter &printer) {
if (!d->activeView) return;
QString title = d->activeView->document()->documentInfo()->aboutInfo("title");
if (title.isEmpty()) {
title = d->activeView->document()->url().fileName();
// strip off the native extension (I don't want foobar.kwd.ps when printing into a file)
KMimeType::Ptr mime = KMimeType::mimeType(d->activeView->document()->outputMimeType());
if (mime) {
QString extension = mime->mainExtension();
if (title.endsWith(extension))
title.chop(extension.length());
}
}
if (title.isEmpty()) {
// #139905
title = i18n("%1 unsaved document (%2)", KisFactory::componentData().aboutData()->programName(),
KGlobal::locale()->formatDate(QDate::currentDate(), KLocale::ShortDate));
}
printer.setDocName(title);
}
void KisMainWindow::createActions()
{
KisActionManager *actionManager = d->viewManager->actionManager();
actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew()));
actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen()));
d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles()));
KSharedConfigPtr configPtr = KisFactory::componentData().config();
d->recentFiles->loadEntries(configPtr->group("RecentFiles"));
d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave()));
d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs()));
d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint()));
d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview()));
d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo()));
d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo()));
d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->exportPdf = new KisAction(i18nc("@action:inmenu", "Export as PDF..."));
d->exportPdf->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->exportPdf->setIcon(themedIcon("application-pdf"));
actionManager->addAction("file_export_pdf", d->exportPdf);
connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf()));
actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit()));
d->closeAll = new KisAction(i18nc("@action:inmenu", "Close All"));
d->closeAll->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->closeAll->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
actionManager->addAction("file_close_all", d->closeAll);
connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
// d->reloadFile = new KisAction(i18nc("@action:inmenu", "Reload"));
// d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED);
// actionManager->addAction("file_reload_file", d->reloadFile);
// connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile()));
d->importFile = new KisAction(themedIcon("document-import"), i18nc("@action:inmenu", "Open ex&isting Document as Untitled Document..."));
actionManager->addAction("file_import_file", d->importFile);
connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
d->exportFile = new KisAction(themedIcon("document-export"), i18nc("@action:inmenu", "E&xport..."));
d->exportFile->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager->addAction("file_export_file", d->exportFile);
connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
/* The following entry opens the document information dialog. Since the action is named so it
intends to show data this entry should not have a trailing ellipses (...). */
d->showDocumentInfo = new KisAction(themedIcon("configure"), i18nc("@action:inmenu", "Document Information"));
d->showDocumentInfo->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager->addAction("file_documentinfo", d->showDocumentInfo);
connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
actionManager->createStandardAction(KStandardAction::KeyBindings, this, SLOT(slotConfigureKeys()));
actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
d->themeManager->registerThemeActions(actionCollection());
connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()));
actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
d->toggleDockers = new KisAction(i18nc("@action:inmenu", "Show Dockers"));
d->toggleDockers->setCheckable(true);
d->toggleDockers->setChecked(true);
actionManager->addAction("view_toggledockers", d->toggleDockers);
connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
d->toggleDockerTitleBars = new KisAction(i18nc("@action:inmenu", "Show Docker Titlebars"));
d->toggleDockerTitleBars->setCheckable(true);
KisConfig cfg;
d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars());
actionManager->addAction("view_toggledockertitlebars", d->toggleDockerTitleBars);
connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool)));
actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
actionCollection()->addAction("window", d->windowMenu);
d->mdiCascade = new KisAction(i18nc("@action:inmenu", "Cascade"));
d->mdiCascade->setActivationFlags(KisAction::MULTIPLE_IMAGES);
actionManager->addAction("windows_cascade", d->mdiCascade);
connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
d->mdiTile = new KisAction(i18nc("@action:inmenu", "Tile"));
d->mdiTile->setActivationFlags(KisAction::MULTIPLE_IMAGES);
actionManager->addAction("windows_tile", d->mdiTile);
connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
d->mdiNextWindow = new KisAction(i18nc("@action:inmenu", "Next"));
d->mdiNextWindow->setActivationFlags(KisAction::MULTIPLE_IMAGES);
actionManager->addAction("windows_next", d->mdiNextWindow);
connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
d->mdiPreviousWindow = new KisAction(i18nc("@action:inmenu", "Previous"));
d->mdiPreviousWindow->setActivationFlags(KisAction::MULTIPLE_IMAGES);
actionCollection()->addAction("windows_previous", d->mdiPreviousWindow);
connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
d->newWindow = new KisAction(themedIcon("window-new"), i18nc("@action:inmenu", "&New Window"));
actionManager->addAction("view_newwindow", d->newWindow);
connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
d->close = new KisAction(i18nc("@action:inmenu", "Close"));
d->close->setActivationFlags(KisAction::ACTIVE_IMAGE);
connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow()));
actionManager->addAction("file_close", d->close);
actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences()));
for (int i = 0; i < 2; i++) {
d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
}
}
void KisMainWindow::applyToolBarLayout()
{
const bool isPlastiqueStyle = style()->objectName() == "plastique";
Q_FOREACH (KToolBar *toolBar, toolBars()) {
toolBar->layout()->setSpacing(4);
if (isPlastiqueStyle) {
toolBar->setContentsMargins(0, 0, 0, 2);
}
}
}
void KisMainWindow::initializeGeometry()
{
// if the user didn's specify the geometry on the command line (does anyone do that still?),
// we first figure out some good default size and restore the x,y position. See bug 285804Z.
KConfigGroup cfg(KGlobal::config(), "MainWindow");
if (!initialGeometrySet()) {
QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
if (!restoreGeometry(geom)) {
const int scnum = QApplication::desktop()->screenNumber(parentWidget());
QRect desk = QApplication::desktop()->availableGeometry(scnum);
// if the desktop is virtual then use virtual screen size
if (QApplication::desktop()->isVirtualDesktop()) {
desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum));
}
quint32 x = desk.x();
quint32 y = desk.y();
quint32 w = 0;
quint32 h = 0;
// Default size -- maximize on small screens, something useful on big screens
const int deskWidth = desk.width();
if (deskWidth > 1024) {
// a nice width, and slightly less than total available
// height to componensate for the window decs
w = (deskWidth / 3) * 2;
h = (desk.height() / 3) * 2;
}
else {
w = desk.width();
h = desk.height();
}
x += (desk.width() - w) / 2;
y += (desk.height() - h) / 2;
move(x,y);
setGeometry(geometry().x(), geometry().y(), w, h);
}
}
restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray())));
}
void KisMainWindow::showManual()
{
QDesktopServices::openUrl(QUrl("https://userbase.kde.org/Special:MyLanguage/Krita/Manual"));
}
void KisMainWindow::showDockerTitleBars(bool show)
{
foreach (QDockWidget *dock, dockWidgets()) {
if (dock->titleBarWidget()) {
const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget();
dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed));
}
}
KisConfig cfg;
cfg.setShowDockerTitleBars(show);
}
void KisMainWindow::moveEvent(QMoveEvent *e)
{
if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) {
KisConfigNotifier::instance()->notifyConfigChanged();
}
}
#include <moc_KisMainWindow.cpp>
diff --git a/krita/ui/KisOpenPane.cpp b/krita/ui/KisOpenPane.cpp
index 38d3c8f4f79..f97e9d54fe0 100644
--- a/krita/ui/KisOpenPane.cpp
+++ b/krita/ui/KisOpenPane.cpp
@@ -1,407 +1,407 @@
/* This file is part of the KDE project
Copyright (C) 2005 Peter Simonsson <psn@linux.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisOpenPane.h"
#include <QLayout>
#include <QLabel>
#include <QImage>
#include <QPainter>
#include <QPen>
#include <QPixmap>
#include <QSize>
#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QStyledItemDelegate>
#include <QLinearGradient>
#include <QDesktopServices>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <klocale.h>
#include <kcomponentdata.h>
#include <kpushbutton.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kurl.h>
#include <KoFileDialog.h>
#include <KoIcon.h>
#include "KisTemplateTree.h"
#include "KisTemplateGroup.h"
#include "KisTemplate.h"
#include "KisDetailsPane.h"
#include "KisTemplatesPane.h"
#include "KisRecentDocumentsPane.h"
#include "ui_KisOpenPaneBase.h"
#include <limits.h>
#include <kconfiggroup.h>
class KoSectionListItem : public QTreeWidgetItem
{
public:
KoSectionListItem(QTreeWidget* treeWidget, const QString& name, int sortWeight, int widgetIndex = -1)
: QTreeWidgetItem(treeWidget, QStringList() << name), m_sortWeight(sortWeight), m_widgetIndex(widgetIndex) {
Qt::ItemFlags newFlags = Qt::NoItemFlags;
if(m_widgetIndex >= 0)
newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
setFlags(newFlags);
}
virtual bool operator<(const QTreeWidgetItem & other) const {
const KoSectionListItem* item = dynamic_cast<const KoSectionListItem*>(&other);
if (!item)
return 0;
return ((item->sortWeight() - sortWeight()) < 0);
}
int sortWeight() const {
return m_sortWeight;
}
int widgetIndex() const {
return m_widgetIndex;
}
private:
int m_sortWeight;
int m_widgetIndex;
};
class KoSectionListDelegate : public QStyledItemDelegate
{
public:
KoSectionListDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { }
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyledItemDelegate::paint(painter, option, index);
if(!(option.state & (int)(QStyle::State_Active && QStyle::State_Enabled)))
{
int ypos = option.rect.y() + ((option.rect.height() - 2) / 2);
QRect lineRect(option.rect.left(), ypos, option.rect.width(), 2);
QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomRight());
gradient.setColorAt(option.direction == Qt::LeftToRight ? 0 : 1, option.palette.color(QPalette::Text));
gradient.setColorAt(option.direction == Qt::LeftToRight ? 1 : 0, Qt::transparent);
painter->fillRect(lineRect, gradient);
}
}
};
class KisOpenPanePrivate : public Ui_KisOpenPaneBase
{
public:
KisOpenPanePrivate() :
Ui_KisOpenPaneBase() {
m_customWidgetsSeparator = 0;
m_templatesSeparator = 0;
}
KComponentData m_componentData;
int m_freeCustomWidgetIndex;
KoSectionListItem* m_customWidgetsSeparator;
KoSectionListItem* m_templatesSeparator;
};
KisOpenPane::KisOpenPane(QWidget *parent, const KComponentData &componentData, const QStringList& mimeFilter, const QString& templatesResourcePath)
: QDialog(parent)
, d(new KisOpenPanePrivate)
{
d->m_componentData = componentData;
d->setupUi(this);
m_mimeFilter = mimeFilter;
d->m_openExistingButton->setText(i18n("Open Existing Document"));
connect(d->m_openExistingButton, SIGNAL(clicked()),
this, SLOT(openFileDialog()));
KoSectionListDelegate* delegate = new KoSectionListDelegate(d->m_sectionList);
d->m_sectionList->setItemDelegate(delegate);
connect(d->m_sectionList, SIGNAL(itemSelectionChanged()),
this, SLOT(updateSelectedWidget()));
connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*, int)),
this, SLOT(itemClicked(QTreeWidgetItem*)));
connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*, int)),
this, SLOT(itemClicked(QTreeWidgetItem*)));
connect(d->cancelButton,SIGNAL(clicked()),this,SLOT(close()));
initRecentDocs();
initTemplates(templatesResourcePath);
d->m_freeCustomWidgetIndex = 4;
if (!d->m_sectionList->selectedItems().isEmpty())
{
KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
if (selectedItem) {
d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
}
}
QList<int> sizes;
// Set the sizes of the details pane splitters
KConfigGroup cfgGrp(d->m_componentData.config(), "TemplateChooserDialog");
sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes);
if (!sizes.isEmpty())
emit splitterResized(0, sizes);
connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)),
this, SLOT(saveSplitterSizes(KisDetailsPane*, const QList<int>&)));
setAcceptDrops(true);
}
KisOpenPane::~KisOpenPane()
{
if (!d->m_sectionList->selectedItems().isEmpty())
{
KoSectionListItem* item = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
if (item) {
if (!qobject_cast<KisDetailsPane*>(d->m_widgetStack->widget(item->widgetIndex()))) {
KConfigGroup cfgGrp(d->m_componentData.config(), "TemplateChooserDialog");
cfgGrp.writeEntry("LastReturnType", item->text(0));
}
}
}
delete d;
}
void KisOpenPane::openFileDialog()
{
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument");
dialog.setCaption(i18n("Open Existing Document"));
dialog.setDefaultDir(qApp->applicationName().contains("krita") || qApp->applicationName().contains("karbon")
? QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)
: QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation));
dialog.setMimeTypeFilters(m_mimeFilter);
dialog.setHideNameFilterDetailsOption();
foreach(KUrl url, dialog.urls()) {
emit openExistingFile(url);
}
}
void KisOpenPane::initRecentDocs()
{
QString header = i18n("Recent Documents");
KisRecentDocumentsPane* recentDocPane = new KisRecentDocumentsPane(this, d->m_componentData, header);
connect(recentDocPane, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openExistingFile(const KUrl&)));
QTreeWidgetItem* item = addPane(header, koIconName("document-open"), recentDocPane, 0);
connect(recentDocPane, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)),
this, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)));
connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)),
recentDocPane, SLOT(resizeSplitter(KisDetailsPane*, const QList<int>&)));
if (d->m_componentData.config()->hasGroup("RecentFiles")) {
d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect);
}
}
void KisOpenPane::initTemplates(const QString& templatesResourcePath)
{
QTreeWidgetItem* selectItem = 0;
QTreeWidgetItem* firstItem = 0;
const int templateOffset = 1000;
if (!templatesResourcePath.isEmpty()) {
KisTemplateTree templateTree(templatesResourcePath, d->m_componentData, true);
foreach (KisTemplateGroup *group, templateTree.groups()) {
if (group->isHidden()) {
continue;
}
if (!d->m_templatesSeparator) {
d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", 999);
}
KisTemplatesPane* pane = new KisTemplatesPane(this, d->m_componentData, group->name(),
group, templateTree.defaultTemplate());
connect(pane, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openTemplate(const KUrl&)));
connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)),
this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)));
connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)),
pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*, const QString&)));
connect(pane, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)),
this, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)));
connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList<int>&)),
pane, SLOT(resizeSplitter(KisDetailsPane*, const QList<int>&)));
QTreeWidgetItem* item = addPane(group->name(), group->templates().first()->loadPicture(),
pane, group->sortingWeight() + templateOffset);
if (!firstItem) {
firstItem = item;
}
if (group == templateTree.defaultGroup()) {
firstItem = item;
}
if (pane->isSelected()) {
selectItem = item;
}
}
} else {
firstItem = d->m_sectionList->topLevelItem(0);
}
KConfigGroup cfgGrp(d->m_componentData.config(), "TemplateChooserDialog");
if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) {
d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect);
} else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) {
d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect);
}
}
void KisOpenPane::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasUrls()) {
event->accept();
}
}
void KisOpenPane::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
// XXX: when the MVC refactoring is done, this can open a bunch of
// urls, but since the part/document combination is still 1:1
// that won't work for now.
emit openExistingFile(event->mimeData()->urls().first());
}
}
void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString& icon)
{
Q_ASSERT(widget);
if (!d->m_customWidgetsSeparator) {
d->m_customWidgetsSeparator = new KoSectionListItem(d->m_sectionList, "", 3);
}
QString realtitle = title;
if (realtitle.isEmpty())
realtitle = i18n("Custom Document");
QTreeWidgetItem* item = addPane(realtitle, icon, widget, d->m_freeCustomWidgetIndex);
++d->m_freeCustomWidgetIndex;
KConfigGroup cfgGrp(d->m_componentData.config(), "TemplateChooserDialog");
QString lastActiveItem = cfgGrp.readEntry("LastReturnType");
bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false);
if (lastActiveItem == realtitle || (lastActiveItem.isEmpty() && showCustomItemByDefault)) {
d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect);
KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
}
}
QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight)
{
if (!widget) {
return 0;
}
int id = d->m_widgetStack->addWidget(widget);
KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id);
listItem->setIcon(0, KIcon(iconName));
return listItem;
}
QTreeWidgetItem* KisOpenPane::addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight)
{
if (!widget) {
return 0;
}
int id = d->m_widgetStack->addWidget(widget);
KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id);
if (!icon.isNull()) {
QImage image = icon.toImage();
if ((image.width() > 48) || (image.height() > 48)) {
image = image.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
image = image.convertToFormat(QImage::Format_ARGB32);
image = image.copy((image.width() - 48) / 2, (image.height() - 48) / 2, 48, 48);
listItem->setIcon(0, QIcon(QPixmap::fromImage(image)));
}
return listItem;
}
void KisOpenPane::updateSelectedWidget()
{
if(!d->m_sectionList->selectedItems().isEmpty())
{
KoSectionListItem* section = dynamic_cast<KoSectionListItem*>(d->m_sectionList->selectedItems().first());
if (!section)
return;
d->m_widgetStack->setCurrentIndex(section->widgetIndex());
}
}
void KisOpenPane::saveSplitterSizes(KisDetailsPane* sender, const QList<int>& sizes)
{
Q_UNUSED(sender);
KConfigGroup cfgGrp(d->m_componentData.config(), "TemplateChooserDialog");
cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes);
}
void KisOpenPane::itemClicked(QTreeWidgetItem* item)
{
KoSectionListItem* selectedItem = static_cast<KoSectionListItem*>(item);
if (selectedItem && selectedItem->widgetIndex() >= 0) {
d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus();
}
}
#include <KisOpenPane.moc>
diff --git a/krita/ui/KisPart.cpp b/krita/ui/KisPart.cpp
index 36e3fe7c536..c57b718569a 100644
--- a/krita/ui/KisPart.cpp
+++ b/krita/ui/KisPart.cpp
@@ -1,644 +1,648 @@
/* This file is part of the KDE project
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* Copyright (C) 2000-2005 David Faure <faure@kde.org>
* Copyright (C) 2007-2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2010-2012 Boudewijn Rempt <boud@kogmbh.com>
* Copyright (C) 2011 Inge Wallin <ingwa@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisPart.h"
#include "KoProgressProxy.h"
#include <KoCanvasController.h>
#include <KoCanvasControllerWidget.h>
#include <KoColorSpaceEngine.h>
#include <KoCanvasBase.h>
#include <KoToolManager.h>
#include <KoInteractionTool.h>
#include <KoShapeBasedDocumentBase.h>
#include <KoResourceServerProvider.h>
#include <kis_icon_utils.h>
#include "KisApplication.h"
#include "KisMainWindow.h"
#include "KisDocument.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "KisOpenPane.h"
#include "KisImportExportManager.h"
#include "dialogs/KisShortcutsDialog.h"
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kstandarddirs.h>
#include <kxmlguifactory.h>
#include <knotification.h>
#include <kdialog.h>
#include <kdesktopfile.h>
#include <QMessageBox>
#include <kmimetype.h>
#include <klocale.h>
#include <kactioncollection.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kshortcut.h>
#include <QDialog>
#include <QGraphicsScene>
#include <QApplication>
#include <QGraphicsProxyWidget>
#include <QDomDocument>
#include <QDomElement>
#include "KisView.h"
#include "KisDocument.h"
#include "kis_factory2.h"
#include "kis_config.h"
#include "kis_clipboard.h"
#include "kis_custom_image_widget.h"
#include "kis_image_from_clipboard_widget.h"
#include "kis_shape_controller.h"
#include "kis_resource_server_provider.h"
+#include "KisImportExportManager.h"
+#include "KisDocumentEntry.h"
#include "kis_color_manager.h"
#include "kis_action.h"
class Q_DECL_HIDDEN KisPart::Private
{
public:
Private(KisPart *_part)
: part(_part)
, canvasItem(0)
, startupWidget(0)
, actionCollection(0)
{
}
~Private()
{
delete canvasItem;
}
KisPart *part;
QList<QPointer<KisView> > views;
QList<QPointer<KisMainWindow> > mainWindows;
QList<QPointer<KisDocument> > documents;
QGraphicsItem *canvasItem;
QString templatesResourcePath;
KisOpenPane *startupWidget;
KActionCollection *actionCollection;
void loadActions();
};
void KisPart::Private::loadActions()
{
- actionCollection = new KActionCollection(part, KisFactory::componentName());
+ actionCollection = new KActionCollection(part, "krita");
KGlobal::dirs()->addResourceType("kis_actions", "data", "krita/actions/");
QStringList actionDefinitions = KGlobal::dirs()->findAllResources("kis_actions", "*.action", KStandardDirs::Recursive | KStandardDirs::NoDuplicates);
foreach(const QString &actionDefinition, actionDefinitions) {
QDomDocument doc;
QFile f(actionDefinition);
f.open(QFile::ReadOnly);
doc.setContent(f.readAll());
QDomElement e = doc.documentElement(); // Actions
QString collection = e.attribute("name");
e = e.firstChild().toElement(); // Action
while (!e.isNull()) {
if (e.tagName() == "Action") {
QString name = e.attribute("name");
QString icon = e.attribute("icon");
QString text = i18n(e.attribute("text").toUtf8().constData());
QString whatsthis = i18n(e.attribute("whatsThis").toUtf8().constData());
QString toolTip = i18n(e.attribute("toolTip").toUtf8().constData());
QString statusTip = i18n(e.attribute("statusTip").toUtf8().constData());
QString iconText = i18n(e.attribute("iconText").toUtf8().constData());
KShortcut shortcut = KShortcut(e.attribute("shortcut"));
bool isCheckable = e.attribute("isCheckable") == "true" ? true : false;
KShortcut defaultShortcut = KShortcut(e.attribute("defaultShortcut"));
if (name.isEmpty()) {
- qDebug() << text << "has no name! From:" << actionDefinition;
+ dbgKrita << text << "has no name! From:" << actionDefinition;
}
KisAction *action = new KisAction(KisIconUtils::loadIcon(icon.toLatin1()), text);
action->setObjectName(name);
action->setWhatsThis(whatsthis);
action->setToolTip(toolTip);
action->setStatusTip(statusTip);
action->setIconText(iconText);
action->setShortcut(shortcut, KAction::ActiveShortcut);
action->setCheckable(isCheckable);
action->setShortcut(defaultShortcut, KAction::DefaultShortcut);
if (!actionCollection->action(name)) {
actionCollection->addAction(name, action);
}
// else {
-// qDebug() << "duplicate action" << name << action << "from" << collection;
+// dbgKrita << "duplicate action" << name << action << "from" << collection;
// delete action;
// }
}
e = e.nextSiblingElement();
}
actionCollection->readSettings();
}
//check for colliding shortcuts
QMap<QKeySequence, QAction*> existingShortcuts;
foreach(QAction* action, actionCollection->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
if (existingShortcuts.contains(action->shortcut())) {
- qDebug() << "action" << action->text() << "and" << existingShortcuts[action->shortcut()]->text() << "have the same shortcut:" << action->shortcut();
+ dbgKrita << "action" << action->text() << "and" << existingShortcuts[action->shortcut()]->text() << "have the same shortcut:" << action->shortcut();
}
else {
existingShortcuts[action->shortcut()] = action;
}
}
}
KisPart* KisPart::instance()
{
K_GLOBAL_STATIC(KisPart, s_instance)
return s_instance;
}
KisPart::KisPart()
: d(new Private(this))
{
setTemplatesResourcePath(QLatin1String("krita/templates/"));
// Preload all the resources in the background
Q_UNUSED(KoResourceServerProvider::instance());
Q_UNUSED(KisResourceServerProvider::instance());
Q_UNUSED(KisColorManager::instance());
}
KisPart::~KisPart()
{
while (!d->documents.isEmpty()) {
delete d->documents.takeFirst();
}
while (!d->views.isEmpty()) {
delete d->views.takeFirst();
}
while (!d->mainWindows.isEmpty()) {
delete d->mainWindows.takeFirst();
}
delete d;
}
void KisPart::addDocument(KisDocument *document)
{
- //qDebug() << "Adding document to part list" << document;
+ //dbgKrita << "Adding document to part list" << document;
Q_ASSERT(document);
if (!d->documents.contains(document)) {
d->documents.append(document);
emit documentOpened('/'+objectName());
}
}
QList<QPointer<KisDocument> > KisPart::documents() const
{
return d->documents;
}
KisDocument *KisPart::createDocument() const
{
KisDocument *doc = new KisDocument();
return doc;
}
int KisPart::documentCount() const
{
return d->documents.size();
}
void KisPart::removeDocument(KisDocument *document)
{
d->documents.removeAll(document);
emit documentClosed('/'+objectName());
document->deleteLater();
}
KisMainWindow *KisPart::createMainWindow()
{
KisMainWindow *mw = new KisMainWindow();
addMainWindow(mw);
return mw;
}
KisView *KisPart::createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent)
{
QApplication::setOverrideCursor(Qt::WaitCursor);
KisView *view = new KisView(document, resourceManager, actionCollection, parent);
QApplication::restoreOverrideCursor();
addView(view);
return view;
}
void KisPart::addView(KisView *view)
{
if (!view)
return;
if (!d->views.contains(view)) {
d->views.append(view);
}
connect(view, SIGNAL(destroyed()), this, SLOT(viewDestroyed()));
emit sigViewAdded(view);
}
void KisPart::removeView(KisView *view)
{
if (!view) return;
emit sigViewRemoved(view);
QPointer<KisDocument> doc = view->document();
d->views.removeAll(view);
if (doc) {
bool found = false;
foreach(QPointer<KisView> view, d->views) {
if (view && view->document() == doc) {
found = true;
break;
}
}
if (!found) {
removeDocument(doc);
}
}
}
QList<QPointer<KisView> > KisPart::views() const
{
return d->views;
}
int KisPart::viewCount(KisDocument *doc) const
{
if (!doc) {
return d->views.count();
}
else {
int count = 0;
foreach(QPointer<KisView> view, d->views) {
if (view->document() == doc) {
count++;
}
}
return count;
}
}
QGraphicsItem *KisPart::canvasItem(KisDocument *document, bool create)
{
if (create && !d->canvasItem) {
d->canvasItem = createCanvasItem(document);
}
return d->canvasItem;
}
QGraphicsItem *KisPart::createCanvasItem(KisDocument *document)
{
if (!document) return 0;
KisView *view = createView(document, 0, 0, 0);
QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget();
QWidget *canvasController = view->findChild<KoCanvasControllerWidget*>();
proxy->setWidget(canvasController);
return proxy;
}
void KisPart::addMainWindow(KisMainWindow *mainWindow)
{
if (!mainWindow) return;
if (d->mainWindows.contains(mainWindow)) return;
- kDebug(30003) <<"mainWindow" << (void*)mainWindow <<"added to doc" << this;
+ dbgUI <<"mainWindow" << (void*)mainWindow <<"added to doc" << this;
d->mainWindows.append(mainWindow);
}
void KisPart::removeMainWindow(KisMainWindow *mainWindow)
{
- kDebug(30003) <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this;
+ dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this;
if (mainWindow) {
d->mainWindows.removeAll(mainWindow);
}
}
const QList<QPointer<KisMainWindow> > &KisPart::mainWindows() const
{
return d->mainWindows;
}
int KisPart::mainwindowCount() const
{
return d->mainWindows.count();
}
KisMainWindow *KisPart::currentMainwindow() const
{
QWidget *widget = qApp->activeWindow();
KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(widget);
while (!mainWindow && widget) {
widget = widget->parentWidget();
mainWindow = qobject_cast<KisMainWindow*>(widget);
}
if (!mainWindow && mainWindows().size() > 0) {
mainWindow = mainWindows().first();
}
return mainWindow;
}
void KisPart::openExistingFile(const KUrl& url)
{
qApp->setOverrideCursor(Qt::BusyCursor);
KisDocument *document = createDocument();
if (!document->openUrl(url)) {
return;
}
document->setModified(false);
addDocument(document);
KisMainWindow *mw = 0;
if (d->startupWidget) {
mw = qobject_cast<KisMainWindow*>(d->startupWidget->parent());
}
if (!mw) {
mw = currentMainwindow();
}
KisView *view = createView(document, mw->resourceManager(), mw->actionCollection(), mw);
mw->addView(view);
if (d->startupWidget) {
d->startupWidget->setParent(0);
d->startupWidget->hide();
}
qApp->restoreOverrideCursor();
}
void KisPart::configureShortcuts()
{
if (!d->actionCollection) {
d->loadActions();
}
// In kdelibs4 a hack was used to hide the shortcut schemes widget in the
// normal shortcut editor KShortcutsDialog from kdelibs, by setting the
// bottom buttons oneself. This does not work anymore (as the buttons are
// no longer exposed), so for now running with a plain copy of the sources
// of KShortcutsDialog, where the schemes editor is disabled directly.
// Not nice, but then soon custom Krita-specific shortcut handling is
// planned anyway.
KisShortcutsDialog dlg(KShortcutsEditor::WidgetAction | KShortcutsEditor::WindowAction | KShortcutsEditor::ApplicationAction);
dlg.addCollection(d->actionCollection);
dlg.configure();
foreach(KisMainWindow *mainWindow, d->mainWindows) {
KActionCollection *ac = mainWindow->actionCollection();
ac->readSettings();
// append shortcuts to tooltips if they exist
foreach( QAction* tempAction, ac->actions())
{
// find the shortcut pattern and delete (note the preceding space in the RegEx)
QString strippedTooltip = tempAction->toolTip().remove(QRegExp("\\s\\(.*\\)"));
// append shortcut if it exists for action
if(tempAction->shortcut() == QKeySequence(0))
tempAction->setToolTip( strippedTooltip);
else
tempAction->setToolTip( strippedTooltip + " (" + tempAction->shortcut().toString() + ")");
}
}
}
void KisPart::openTemplate(const KUrl& url)
{
qApp->setOverrideCursor(Qt::BusyCursor);
KisDocument *document = createDocument();
bool ok = document->loadNativeFormat(url.toLocalFile());
document->setModified(false);
document->undoStack()->clear();
if (ok) {
QString mimeType = KMimeType::findByUrl( url, 0, true )->name();
// in case this is a open document template remove the -template from the end
mimeType.remove( QRegExp( "-template$" ) );
document->setMimeTypeAfterLoading(mimeType);
document->resetURL();
document->setEmpty();
} else {
document->showLoadingErrorDialog();
document->initEmpty();
}
addDocument(document);
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->startupWidget->parent());
if (!mw) mw = currentMainwindow();
KisView *view = createView(document, mw->resourceManager(), mw->actionCollection(), mw);
mw->addView(view);
d->startupWidget->setParent(0);
d->startupWidget->hide();
qApp->restoreOverrideCursor();
}
void KisPart::viewDestroyed()
{
KisView *view = qobject_cast<KisView*>(sender());
if (view) {
removeView(view);
}
}
void KisPart::addRecentURLToAllMainWindows(KUrl url)
{
// Add to recent actions list in our mainWindows
foreach(KisMainWindow *mainWindow, d->mainWindows) {
mainWindow->addRecentURL(url);
}
}
void KisPart::showStartUpWidget(KisMainWindow *mainWindow, bool alwaysShow)
{
#ifndef NDEBUG
if (d->templatesResourcePath.isEmpty())
- kDebug(30003) << "showStartUpWidget called, but setTemplatesResourcePath() never called. This will not show a lot";
+ dbgUI << "showStartUpWidget called, but setTemplatesResourcePath() never called. This will not show a lot";
#endif
if (!alwaysShow) {
KConfigGroup cfgGrp(KisFactory::componentData().config(), "TemplateChooserDialog");
QString fullTemplateName = cfgGrp.readPathEntry("AlwaysUseTemplate", QString());
if (!fullTemplateName.isEmpty()) {
KUrl url(fullTemplateName);
QFileInfo fi(url.toLocalFile());
if (!fi.exists()) {
const QString templatesResourcePath = this->templatesResourcePath();
QString desktopfile = KGlobal::dirs()->findResource("data", templatesResourcePath + "*/" + fullTemplateName);
if (desktopfile.isEmpty()) {
desktopfile = KGlobal::dirs()->findResource("data", templatesResourcePath + fullTemplateName);
}
if (desktopfile.isEmpty()) {
fullTemplateName.clear();
} else {
KUrl templateURL;
KDesktopFile f(desktopfile);
templateURL.setPath(KUrl(desktopfile).directory() + '/' + f.readUrl());
fullTemplateName = templateURL.toLocalFile();
}
}
if (!fullTemplateName.isEmpty()) {
openTemplate(fullTemplateName);
return;
}
}
}
if (d->startupWidget) {
delete d->startupWidget;
}
- const QStringList mimeFilter = koApp->mimeFilter(KisImportExportManager::Import);
+ const QStringList mimeFilter = KisImportExportManager::mimeFilter(KIS_MIME_TYPE,
+ KisImportExportManager::Import,
+ KisDocumentEntry::extraNativeMimeTypes());
d->startupWidget = new KisOpenPane(0, KisFactory::componentData(), mimeFilter, d->templatesResourcePath);
d->startupWidget->setWindowModality(Qt::WindowModal);
QList<CustomDocumentWidgetItem> widgetList = createCustomDocumentWidgets(d->startupWidget);
foreach(const CustomDocumentWidgetItem & item, widgetList) {
d->startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon);
connect(item.widget, SIGNAL(documentSelected(KisDocument*)), this, SLOT(startCustomDocument(KisDocument*)));
}
connect(d->startupWidget, SIGNAL(openExistingFile(const KUrl&)), this, SLOT(openExistingFile(const KUrl&)));
connect(d->startupWidget, SIGNAL(openTemplate(const KUrl&)), this, SLOT(openTemplate(const KUrl&)));
d->startupWidget->setParent(mainWindow);
d->startupWidget->setWindowFlags(Qt::Dialog);
d->startupWidget->exec();
}
QList<KisPart::CustomDocumentWidgetItem> KisPart::createCustomDocumentWidgets(QWidget * parent)
{
KisConfig cfg;
int w = cfg.defImageWidth();
int h = cfg.defImageHeight();
QList<KisPart::CustomDocumentWidgetItem> widgetList;
{
KisPart::CustomDocumentWidgetItem item;
item.widget = new KisCustomImageWidget(parent,
w, h, cfg.defImageResolution(), cfg.defColorModel(), cfg.defaultColorDepth(), cfg.defColorProfile(),
i18n("Unnamed"));
item.icon = "application-x-krita";
widgetList << item;
}
{
QSize sz = KisClipboard::instance()->clipSize();
if (sz.isValid() && sz.width() != 0 && sz.height() != 0) {
w = sz.width();
h = sz.height();
}
KisPart::CustomDocumentWidgetItem item;
item.widget = new KisImageFromClipboard(parent,
w, h, cfg.defImageResolution(), cfg.defColorModel(), cfg.defaultColorDepth(), cfg.defColorProfile(),
i18n("Unnamed"));
item.title = i18n("Create from Clipboard");
item.icon = "klipper";
widgetList << item;
}
return widgetList;
}
void KisPart::setTemplatesResourcePath(const QString &templatesResourcePath)
{
Q_ASSERT(!templatesResourcePath.isEmpty());
Q_ASSERT(templatesResourcePath.endsWith(QLatin1Char('/')));
d->templatesResourcePath = templatesResourcePath;
}
QString KisPart::templatesResourcePath() const
{
return d->templatesResourcePath;
}
void KisPart::startCustomDocument(KisDocument* doc)
{
addDocument(doc);
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->startupWidget->parent());
if (!mw) mw = currentMainwindow();
KisView *view = createView(doc, mw->resourceManager(), mw->actionCollection(), mw);
mw->addView(view);
d->startupWidget->setParent(0);
d->startupWidget->hide();
}
KisInputManager* KisPart::currentInputManager()
{
return instance()->currentMainwindow()->viewManager()->inputManager();
}
#include <KisPart.moc>
diff --git a/krita/ui/KisTemplate.cpp b/krita/ui/KisTemplate.cpp
index d9956d254d9..bc888763e90 100644
--- a/krita/ui/KisTemplate.cpp
+++ b/krita/ui/KisTemplate.cpp
@@ -1,61 +1,61 @@
/* This file is part of the KDE project
Copyright (C) 2000 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisTemplate.h"
#include <QImage>
#include <QPixmap>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kiconloader.h>
KisTemplate::KisTemplate(const QString &name, const QString &description, const QString &file,
const QString &picture, const QString &fileName, const QString &_measureSystem,
bool hidden, bool touched) :
m_name(name), m_descr(description), m_file(file), m_picture(picture), m_fileName(fileName),
m_hidden(hidden), m_touched(touched), m_cached(false), m_measureSystem(_measureSystem)
{
}
const QPixmap &KisTemplate::loadPicture()
{
if (m_cached)
return m_pixmap;
m_cached = true;
if (m_picture[ 0 ] == '/') {
QImage img(m_picture);
if (img.isNull()) {
- kWarning() << "Couldn't find icon " << m_picture;
+ dbgKrita << "Couldn't find icon " << m_picture;
m_pixmap = QPixmap();
return m_pixmap;
}
const int maxHeightWidth = 128; // ### TODO: some people would surely like to have 128x128
if (img.width() > maxHeightWidth || img.height() > maxHeightWidth) {
img = img.scaled(maxHeightWidth, maxHeightWidth, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
m_pixmap = QPixmap::fromImage(img);
return m_pixmap;
} else { // relative path
m_pixmap = KIconLoader::global()->loadIcon(m_picture, KIconLoader::Desktop, 128);
return m_pixmap;
}
}
diff --git a/krita/ui/KisTemplateCreateDia.cpp b/krita/ui/KisTemplateCreateDia.cpp
index e476397a7de..f63256a706c 100644
--- a/krita/ui/KisTemplateCreateDia.cpp
+++ b/krita/ui/KisTemplateCreateDia.cpp
@@ -1,542 +1,542 @@
/*
This file is part of the KDE project
Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
2000 Werner Trobin <trobin@kde.org>
Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <KisTemplateCreateDia.h>
#include <QFile>
#include <QLabel>
#include <QRadioButton>
#include <QPushButton>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QPixmap>
#include <QHBoxLayout>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QGroupBox>
#include <QTemporaryFile>
#include <klineedit.h>
#include <klocale.h>
#include <KoIcon.h>
#include <KisDocument.h>
#include <KisTemplates.h>
#include <KisTemplateTree.h>
#include <KisTemplateGroup.h>
#include <KisTemplate.h>
#include <kicondialog.h>
#include <kinputdialog.h>
#include <QMessageBox>
#include <kstandarddirs.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kiconloader.h>
#include <kaboutdata.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include <kis_factory2.h>
#include <kurl.h>
#include <k4aboutdata.h>
// ODF thumbnail extent
static const int thumbnailExtent = 128;
class KisTemplateCreateDiaPrivate {
public:
KisTemplateCreateDiaPrivate(const KComponentData &componentData, const QString &filePath, const QPixmap &thumbnail)
: m_componentData( componentData )
, m_filePath(filePath)
, m_thumbnail(thumbnail)
{
m_tree=0;
m_name=0;
m_default=0;
m_custom=0;
m_select=0;
m_preview=0;
m_groups=0;
m_add=0;
m_remove=0;
m_defaultTemplate=0;
}
~KisTemplateCreateDiaPrivate() {
delete m_tree;
}
KisTemplateTree *m_tree;
KLineEdit *m_name;
QRadioButton *m_default, *m_custom;
QPushButton *m_select;
QLabel *m_preview;
QString m_customFile;
QPixmap m_customPixmap;
QTreeWidget *m_groups;
QPushButton *m_add, *m_remove;
QCheckBox *m_defaultTemplate;
KComponentData m_componentData;
QString m_filePath;
QPixmap m_thumbnail;
bool m_changed;
};
/****************************************************************************
*
* Class: koTemplateCreateDia
*
****************************************************************************/
KisTemplateCreateDia::KisTemplateCreateDia(const QString &templatesResourcePath, const KComponentData &componentData,
const QString &filePath, const QPixmap &thumbnail, QWidget *parent)
: KDialog(parent)
, d(new KisTemplateCreateDiaPrivate(componentData, filePath, thumbnail))
{
setButtons( KDialog::Ok|KDialog::Cancel );
setDefaultButton( KDialog::Ok );
setCaption( i18n( "Create Template" ) );
setModal( true );
setObjectName( "template create dia" );
QWidget *mainwidget=mainWidget();
QHBoxLayout *mbox=new QHBoxLayout( mainwidget );
QVBoxLayout* leftbox = new QVBoxLayout();
mbox->addLayout( leftbox );
QLabel *label=new QLabel(i18nc("Template name", "Name:"), mainwidget);
QHBoxLayout *namefield=new QHBoxLayout();
leftbox->addLayout( namefield );
namefield->addWidget(label);
d->m_name=new KLineEdit(mainwidget);
d->m_name->setFocus();
connect(d->m_name, SIGNAL(textChanged(const QString &)),
this, SLOT(slotNameChanged(const QString &)));
namefield->addWidget(d->m_name);
label=new QLabel(i18n("Group:"), mainwidget);
leftbox->addWidget(label);
d->m_groups = new QTreeWidget(mainwidget);
leftbox->addWidget(d->m_groups);
d->m_groups->setColumnCount(1);
d->m_groups->setHeaderHidden(true);
d->m_groups->setRootIsDecorated(true);
d->m_groups->setSortingEnabled(true);
d->m_tree = new KisTemplateTree(templatesResourcePath, componentData, true);
fillGroupTree();
d->m_groups->sortItems(0, Qt::AscendingOrder);
QHBoxLayout *bbox=new QHBoxLayout();
leftbox->addLayout( bbox );
d->m_add=new QPushButton(i18n("&Add Group..."), mainwidget);
connect(d->m_add, SIGNAL(clicked()), this, SLOT(slotAddGroup()));
bbox->addWidget(d->m_add);
d->m_remove=new QPushButton(i18n("&Remove"), mainwidget);
connect(d->m_remove, SIGNAL(clicked()), this, SLOT(slotRemove()));
bbox->addWidget(d->m_remove);
QVBoxLayout *rightbox=new QVBoxLayout();
mbox->addLayout( rightbox );
QGroupBox *pixbox = new QGroupBox(i18n("Picture"), mainwidget);
rightbox->addWidget(pixbox);
QVBoxLayout *pixlayout=new QVBoxLayout(pixbox );
d->m_default=new QRadioButton(i18n("&Preview"), pixbox);
d->m_default->setChecked(true);
connect(d->m_default, SIGNAL(clicked()), this, SLOT(slotDefault()));
pixlayout->addWidget(d->m_default);
QHBoxLayout *custombox=new QHBoxLayout();
d->m_custom=new QRadioButton(i18n("Custom:"), pixbox);
d->m_custom->setChecked(false);
connect(d->m_custom, SIGNAL(clicked()), this, SLOT(slotCustom()));
custombox->addWidget(d->m_custom);
d->m_select=new QPushButton(i18n("&Select..."), pixbox);
connect(d->m_select, SIGNAL(clicked()), this, SLOT(slotSelect()));
custombox->addWidget(d->m_select);
custombox->addStretch(1);
pixlayout->addLayout(custombox);
d->m_preview=new QLabel(pixbox); // setPixmap() -> auto resize?
pixlayout->addWidget(d->m_preview, 0, Qt::AlignCenter);
pixlayout->addStretch(1);
d->m_defaultTemplate = new QCheckBox( i18n("Use the new template as default"), mainwidget );
d->m_defaultTemplate->setChecked( true );
d->m_defaultTemplate->setToolTip( i18n("Use the new template every time %1 starts", KisFactory::componentData().aboutData()->programName() ) );
rightbox->addWidget( d->m_defaultTemplate );
enableButtonOk(false);
d->m_changed=false;
updatePixmap();
connect(d->m_groups, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
d->m_remove->setEnabled(d->m_groups->currentItem());
connect(this,SIGNAL(okClicked()),this,SLOT(slotOk()));
}
KisTemplateCreateDia::~KisTemplateCreateDia() {
delete d;
}
void KisTemplateCreateDia::slotSelectionChanged()
{
const QTreeWidgetItem* item = d->m_groups->currentItem();
d->m_remove->setEnabled( item );
if ( ! item )
return;
if ( item->parent() != NULL )
{
d->m_name->setText( item->text( 0 ) );
}
}
void KisTemplateCreateDia::createTemplate(const QString &templatesResourcePath,
const char *suffix,
const KComponentData &componentData,
KisDocument *document, QWidget *parent)
{
QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + QLatin1String(QLatin1String(suffix)));
//Check that creation of temp file was successful
if (!tempFile->open()) {
delete tempFile;
qWarning("Creation of temporary file to store template failed.");
return;
}
const QString fileName = tempFile->fileName();
tempFile->close(); // need to close on Windows before we can open it again to save
delete tempFile; // now the file has disappeared and we can create a new file with the generated name
document->saveNativeFormat(fileName);
const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent));
KisTemplateCreateDia *dia = new KisTemplateCreateDia(templatesResourcePath, componentData, fileName, thumbnail, parent);
dia->exec();
delete dia;
QDir d;
d.remove(fileName);
}
static void
saveAsQuadraticPng(const QPixmap &pixmap, const QString &fileName)
{
QImage icon = pixmap.toImage();
icon = icon.convertToFormat(QImage::Format_ARGB32);
const int iconExtent = qMax(icon.width(), icon.height());
icon = icon.copy((icon.width() - iconExtent) / 2, (icon.height() - iconExtent) / 2, iconExtent, iconExtent);
icon.save(fileName, "PNG");
}
void KisTemplateCreateDia::slotOk() {
// get the current item, if there is one...
QTreeWidgetItem *item = d->m_groups->currentItem();
if(!item)
item = d->m_groups->topLevelItem(0);
if(!item) { // safe :)
d->m_tree->writeTemplateTree();
slotButtonClicked( KDialog::Cancel );
return;
}
// is it a group or a template? anyway - get the group :)
if(item->parent() != NULL)
item=item->parent();
if(!item) { // *very* safe :P
d->m_tree->writeTemplateTree();
slotButtonClicked( KDialog::Cancel );
return;
}
KisTemplateGroup *group=d->m_tree->find(item->text(0));
if(!group) { // even safer
d->m_tree->writeTemplateTree();
slotButtonClicked( KDialog::Cancel );
return;
}
if(d->m_name->text().isEmpty()) {
d->m_tree->writeTemplateTree();
slotButtonClicked( KDialog::Cancel );
return;
}
// copy the tmp file and the picture the app provides
QString dir = KGlobal::dirs()->saveLocation("data", d->m_tree->templatesResourcePath());
dir+=group->name();
QString templateDir=dir+"/.source/";
QString iconDir=dir+"/.icon/";
QString file=KisTemplates::trimmed(d->m_name->text());
QString tmpIcon=".icon/"+file;
tmpIcon+=".png";
QString icon=iconDir+file;
icon+=".png";
// try to find the extension for the template file :P
const int pos = d->m_filePath.lastIndexOf(QLatin1Char('.'));
QString ext;
if ( pos > -1 )
ext = d->m_filePath.mid(pos);
else
- kWarning(30004) << "Template extension not found!";
+ warnUI << "Template extension not found!";
KUrl dest;
dest.setPath(templateDir+file+ext);
if (QFile::exists( dest.pathOrUrl())) {
do {
file.prepend( '_' );
dest.setPath( templateDir + file + ext );
tmpIcon=".icon/" + file + ".png";
icon=iconDir + file + ".png";
}
while (QFile(dest.toLocalFile()).exists());
}
bool ignore = false;
- kDebug(30004) <<"Trying to create template:" << d->m_name->text() <<"URL=" <<".source/"+file+ext <<" ICON=" << tmpIcon;
+ dbgUI <<"Trying to create template:" << d->m_name->text() <<"URL=" <<".source/"+file+ext <<" ICON=" << tmpIcon;
KisTemplate *t=new KisTemplate(d->m_name->text(), QString(), ".source/"+file+ext, tmpIcon, "", "", false, true);
if (!group->add(t)) {
KisTemplate *existingTemplate=group->find(d->m_name->text());
if (existingTemplate && !existingTemplate->isHidden()) {
if (QMessageBox::warning(this,
i18nc("@title:window", "Krita"),
i18n("Do you really want to overwrite the existing '%1' template?", existingTemplate->name()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
group->add(t, true);
}
else {
delete t;
return;
}
}
else {
ignore = true;
}
}
if (!KStandardDirs::makeDir(templateDir) || !KStandardDirs::makeDir(iconDir)) {
d->m_tree->writeTemplateTree();
slotButtonClicked( KDialog::Cancel );
return;
}
KUrl orig;
orig.setPath(d->m_filePath);
// don't overwrite the hidden template file with a new non-hidden one
if ( !ignore )
{
QFile::copy(d->m_filePath, dest.toLocalFile());
// save the picture as icon
// (needs to be square, otherwise KIconLoader dpes nasty changes)
if(d->m_default->isChecked() && !d->m_thumbnail.isNull()) {
saveAsQuadraticPng(d->m_thumbnail, icon);
} else if(!d->m_customPixmap.isNull()) {
saveAsQuadraticPng(d->m_customPixmap, icon);
} else {
- kWarning(30004) << "Could not save the preview picture!";
+ warnUI << "Could not save the preview picture!";
}
}
// if there's a .directory file, we copy this one, too
bool ready=false;
QStringList tmp=group->dirs();
for(QStringList::ConstIterator it=tmp.constBegin(); it!=tmp.constEnd() && !ready; ++it) {
if((*it).contains(dir)==0) {
orig.setPath( (*it)+".directory" );
// Check if we can read the file
if (QFile(orig.toLocalFile()).exists()) {
dest.setPath(dir + "/.directory");
// We copy the file with overwrite
if (!QFile(orig.toLocalFile()).copy(dest.toLocalFile())) {
- qWarning() << "Failed to copy from" << orig.toLocalFile() << "to" << dest.toLocalFile();
+ warnKrita << "Failed to copy from" << orig.toLocalFile() << "to" << dest.toLocalFile();
}
ready = true;
}
}
}
d->m_tree->writeTemplateTree();
if ( d->m_defaultTemplate->isChecked() )
{
KConfigGroup grp( d->m_componentData.config(), "TemplateChooserDialog" );
grp.writeEntry( "LastReturnType", "Template" );
grp.writePathEntry( "FullTemplateName", dir + '/' + t->file() );
grp.writePathEntry( "AlwaysUseTemplate", dir + '/' + t->file() );
}
}
void KisTemplateCreateDia::slotDefault() {
d->m_default->setChecked(true);
d->m_custom->setChecked(false);
updatePixmap();
}
void KisTemplateCreateDia::slotCustom() {
d->m_default->setChecked(false);
d->m_custom->setChecked(true);
if(d->m_customFile.isEmpty())
slotSelect();
else
updatePixmap();
}
void KisTemplateCreateDia::slotSelect() {
d->m_default->setChecked(false);
d->m_custom->setChecked(true);
QString name = KIconDialog::getIcon();
if( name.isEmpty() ) {
if(d->m_customFile.isEmpty()) {
d->m_default->setChecked(true);
d->m_custom->setChecked(false);
}
return;
}
const QString path = KIconLoader::global()->iconPath(name, -thumbnailExtent);
d->m_customFile = path;
d->m_customPixmap=QPixmap();
updatePixmap();
}
void KisTemplateCreateDia::slotNameChanged(const QString &name) {
if( ( name.trimmed().isEmpty() || !d->m_groups->topLevelItem(0) ) && !d->m_changed )
enableButtonOk(false);
else
enableButtonOk(true);
}
void KisTemplateCreateDia::slotAddGroup() {
bool ok=false;
const QString name ( KInputDialog::getText( i18n("Add Group"), i18n("Enter group name:"), QString(), &ok, this ) );
if(!ok)
return;
KisTemplateGroup *group=d->m_tree->find(name);
if(group && !group->isHidden())
{
QMessageBox::information( this, i18n("This name is already used."), i18n("Add Group") );
return;
}
QString dir = KGlobal::dirs()->saveLocation("data", d->m_tree->templatesResourcePath());
dir+=name;
KisTemplateGroup *newGroup=new KisTemplateGroup(name, dir, 0, true);
d->m_tree->add(newGroup);
QTreeWidgetItem *item = new QTreeWidgetItem(d->m_groups, QStringList() << name);
d->m_groups->setCurrentItem(item);
d->m_groups->sortItems(0, Qt::AscendingOrder);
d->m_name->setFocus();
enableButtonOk(true);
d->m_changed=true;
}
void KisTemplateCreateDia::slotRemove() {
QTreeWidgetItem *item = d->m_groups->currentItem();
if(!item)
return;
QString what;
QString removed;
if (item->parent() == NULL) {
what = i18n("Do you really want to remove that group?");
removed = i18nc("@title:window", "Remove Group");
} else {
what = i18n("Do you really want to remove that template?");
removed = i18nc("@title:window", "Remove Template");
}
if (QMessageBox::warning(this,
removed,
what,
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox:: No) {
d->m_name->setFocus();
return;
}
if(item->parent() == NULL) {
KisTemplateGroup *group=d->m_tree->find(item->text(0));
if(group)
group->setHidden(true);
}
else {
bool done=false;
QList<KisTemplateGroup*> groups = d->m_tree->groups();
QList<KisTemplateGroup*>::const_iterator it = groups.constBegin();
for(; it != groups.constEnd() && !done; ++it) {
KisTemplate *t = (*it)->find(item->text(0));
if(t) {
t->setHidden(true);
done=true;
}
}
}
delete item;
item=0;
enableButtonOk(true);
d->m_name->setFocus();
d->m_changed=true;
}
void KisTemplateCreateDia::updatePixmap() {
if(d->m_default->isChecked() && !d->m_thumbnail.isNull())
d->m_preview->setPixmap(d->m_thumbnail);
else if(d->m_custom->isChecked() && !d->m_customFile.isEmpty()) {
if(d->m_customPixmap.isNull()) {
- kDebug(30004) <<"Trying to load picture" << d->m_customFile;
+ dbgUI <<"Trying to load picture" << d->m_customFile;
// use the code in KisTemplate to load the image... hacky, I know :)
KisTemplate t("foo", "bar", QString(), d->m_customFile);
d->m_customPixmap=t.loadPicture();
}
else
- kWarning(30004) << "Trying to load picture";
+ warnUI << "Trying to load picture";
if(!d->m_customPixmap.isNull())
d->m_preview->setPixmap(d->m_customPixmap);
else
d->m_preview->setText(i18n("Could not load picture."));
}
else
d->m_preview->setText(i18n("No picture available."));
}
void KisTemplateCreateDia::fillGroupTree() {
foreach(KisTemplateGroup *group, d->m_tree->groups()) {
if(group->isHidden())
continue;
QTreeWidgetItem *groupItem=new QTreeWidgetItem(d->m_groups, QStringList() << group->name());
foreach(KisTemplate *t, group->templates()) {
if(t->isHidden())
continue;
(void)new QTreeWidgetItem(groupItem, QStringList() << t->name());
}
}
}
#include <KisTemplateCreateDia.moc>
diff --git a/krita/ui/KisTemplateGroup.cpp b/krita/ui/KisTemplateGroup.cpp
index c5bcbe208fb..2a32d426efc 100644
--- a/krita/ui/KisTemplateGroup.cpp
+++ b/krita/ui/KisTemplateGroup.cpp
@@ -1,96 +1,96 @@
/* This file is part of the KDE project
Copyright (C) 2000 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisTemplateGroup.h"
#include <KisTemplate.h>
#include <QFile>
KisTemplateGroup::KisTemplateGroup(const QString &name, const QString &dir,
int _sortingWeight, bool touched) :
m_name(name), m_touched(touched), m_sortingWeight(_sortingWeight)
{
m_dirs.append(dir);
}
KisTemplateGroup::~KisTemplateGroup()
{
qDeleteAll(m_templates);
}
bool KisTemplateGroup::isHidden() const
{
QList<KisTemplate*>::const_iterator it = m_templates.begin();
bool hidden = true;
while (it != m_templates.end() && hidden) {
hidden = (*it)->isHidden();
++it;
}
return hidden;
}
void KisTemplateGroup::setHidden(bool hidden) const
{
foreach (KisTemplate* t, m_templates)
t->setHidden(hidden);
m_touched = true;
}
bool KisTemplateGroup::add(KisTemplate *t, bool force, bool touch)
{
KisTemplate *myTemplate = find(t->name());
if (myTemplate == 0) {
m_templates.append(t);
m_touched = touch;
return true;
} else if (myTemplate && force) {
- //kDebug( 30003 ) <<"removing :" << myTemplate->fileName();
+ //dbgUI <<"removing :" << myTemplate->fileName();
QFile::remove(myTemplate->fileName());
QFile::remove(myTemplate->picture());
QFile::remove(myTemplate->file());
m_templates.removeAll(myTemplate);
delete myTemplate;
m_templates.append(t);
m_touched = touch;
return true;
}
return false;
}
KisTemplate *KisTemplateGroup::find(const QString &name) const
{
QList<KisTemplate*>::const_iterator it = m_templates.begin();
KisTemplate* ret = NULL;
while (it != m_templates.end()) {
if ((*it)->name() == name) {
ret = *it;
break;
}
++it;
}
return ret;
}
diff --git a/krita/ui/KisTemplateTree.cpp b/krita/ui/KisTemplateTree.cpp
index f0e8c500fe7..9ebd22d2da8 100644
--- a/krita/ui/KisTemplateTree.cpp
+++ b/krita/ui/KisTemplateTree.cpp
@@ -1,289 +1,289 @@
/* This file is part of the KDE project
Copyright (C) 2000 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisTemplateTree.h"
#include <QDir>
#include <QPrinter>
#include <QUrl>
#include <kdesktopfile.h>
#include <kconfig.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kio/netaccess.h>
#include <klocale.h>
#include <kconfiggroup.h>
#include <KisTemplate.h>
#include <KisTemplateGroup.h>
#include <KisTemplates.h>
KisTemplateTree::KisTemplateTree(const QString &templatesResourcePath,
const KComponentData &componentData, bool readTree) :
m_templatesResourcePath(templatesResourcePath), m_componentData(componentData), m_defaultGroup(0),
m_defaultTemplate(0)
{
if (readTree)
readTemplateTree();
}
KisTemplateTree::~KisTemplateTree()
{
qDeleteAll(m_groups);
}
void KisTemplateTree::readTemplateTree()
{
readGroups();
readTemplates();
}
void KisTemplateTree::writeTemplateTree()
{
QString localDir = KGlobal::dirs()->saveLocation("data", m_templatesResourcePath);
foreach (KisTemplateGroup *group, m_groups) {
- //kDebug( 30003 ) <<"---------------------------------";
- //kDebug( 30003 ) <<"group:" << group->name();
+ //dbgUI <<"---------------------------------";
+ //dbgUI <<"group:" << group->name();
bool touched = false;
QList<KisTemplate*> templates = group->templates();
QList<KisTemplate*>::iterator it = templates.begin();
for (; it != templates.end() && !touched && !group->touched(); ++it)
touched = (*it)->touched();
if (group->touched() || touched) {
- //kDebug( 30003 ) <<"touched";
+ //dbgUI <<"touched";
if (!group->isHidden()) {
- //kDebug( 30003 ) <<"not hidden";
+ //dbgUI <<"not hidden";
KStandardDirs::makeDir(localDir + group->name()); // create the local group dir
} else {
- //kDebug( 30003 ) <<"hidden";
+ //dbgUI <<"hidden";
if (group->dirs().count() == 1 && group->dirs().contains(localDir)) {
- //kDebug( 30003 ) <<"local only";
+ //dbgUI <<"local only";
KIO::NetAccess::del(QUrl::fromLocalFile(group->dirs().first()), 0);
- //kDebug( 30003 ) <<"removing:" << group->dirs().first();
+ //dbgUI <<"removing:" << group->dirs().first();
} else {
- //kDebug( 30003 ) <<"global";
+ //dbgUI <<"global";
KStandardDirs::makeDir(localDir + group->name());
}
}
}
foreach (KisTemplate *t, templates) {
if (t->touched()) {
- //kDebug( 30003 ) <<"++template:" << t->name();
+ //dbgUI <<"++template:" << t->name();
writeTemplate(t, group, localDir);
}
if (t->isHidden() && t->touched()) {
- //kDebug( 30003 ) <<"+++ delete local template ##############";
+ //dbgUI <<"+++ delete local template ##############";
writeTemplate(t, group, localDir);
QFile::remove(t->file());
QFile::remove(t->picture());
}
}
}
}
void KisTemplateTree::add(KisTemplateGroup *g)
{
KisTemplateGroup *group = find(g->name());
if (group == NULL)
m_groups.append(g);
else {
group->addDir(g->dirs().first()); // "...there can be only one..." (Queen)
delete g;
g = NULL;
}
}
KisTemplateGroup *KisTemplateTree::find(const QString &name) const
{
QList<KisTemplateGroup*>::const_iterator it = m_groups.begin();
KisTemplateGroup* ret = NULL;
while (it != m_groups.end()) {
if ((*it)->name() == name) {
ret = *it;
break;
}
++it;
}
return ret;
}
void KisTemplateTree::readGroups()
{
QStringList dirs = KGlobal::dirs()->findDirs("data", m_templatesResourcePath);
foreach(const QString & dirName, dirs) {
- //kDebug( 30003 ) <<"dir:" << *it;
+ //dbgUI <<"dir:" << *it;
QDir dir(dirName);
// avoid the annoying warning
if (!dir.exists())
continue;
QStringList templateDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(const QString & templateDirName, templateDirs) {
QDir templateDir(dirName + templateDirName);
QString name = templateDirName;
QString defaultTab;
int sortingWeight = 1000;
if (templateDir.exists(".directory")) {
KDesktopFile config(templateDir.absoluteFilePath(".directory"));
KConfigGroup dg = config.desktopGroup();
name = dg.readEntry("Name");
defaultTab = dg.readEntry("X-KDE-DefaultTab");
sortingWeight = dg.readEntry("X-KDE-SortingWeight", 1000);
- //kDebug( 30003 ) <<"name:" << name;
+ //dbgUI <<"name:" << name;
}
KisTemplateGroup *g = new KisTemplateGroup(name, templateDir.absolutePath() + QDir::separator(), sortingWeight);
add(g);
if (defaultTab == "true")
m_defaultGroup = g;
}
}
}
void KisTemplateTree::readTemplates()
{
QString dontShow = "imperial";
if (KGlobal::locale()->pageSize() == QPrinter::Letter) {
dontShow = "metric";
}
foreach (KisTemplateGroup* group, m_groups) {
QStringList dirs = group->dirs();
for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) {
QDir d(*it);
if (!d.exists())
continue;
QStringList files = d.entryList(QDir::Files | QDir::Readable, QDir::Name);
for (int i = 0; i < files.count(); ++i) {
QString filePath = *it + files[i];
- //kDebug( 30003 ) <<"filePath:" << filePath;
+ //dbgUI <<"filePath:" << filePath;
QString icon;
QString text;
QString description;
QString hidden_str;
QString fileName;
bool hidden = false;
bool defaultTemplate = false;
QString templatePath;
QString measureSystem;
// If a desktop file, then read the name from it.
// Otherwise (or if no name in it?) use file name
if (KDesktopFile::isDesktopFile(filePath)) {
KConfig _config(filePath, KConfig::SimpleConfig);
KConfigGroup config(&_config, "Desktop Entry");
if (config.readEntry("Type") == "Link") {
text = config.readEntry("Name");
fileName = filePath;
description = config.readEntry("Comment");
- //kDebug( 30003 ) <<"name:" << text;
+ //dbgUI <<"name:" << text;
icon = config.readEntry("Icon");
if (icon[0] != '/' && // allow absolute paths for icons
QFile::exists(*it + icon)) // allow icons from icontheme
icon = *it + icon;
- //kDebug( 30003 ) <<"icon2:" << icon;
+ //dbgUI <<"icon2:" << icon;
hidden = config.readEntry("X-KDE-Hidden", false);
defaultTemplate = config.readEntry("X-KDE-DefaultTemplate", false);
measureSystem = config.readEntry("X-KDE-MeasureSystem").toLower();
// Don't add a template that is for the wrong measure system
if (measureSystem == dontShow)
continue;
- //kDebug( 30003 ) <<"hidden:" << hidden_str;
+ //dbgUI <<"hidden:" << hidden_str;
templatePath = config.readPathEntry("URL", QString());
- //kDebug( 30003 ) <<"Link to :" << templatePath;
+ //dbgUI <<"Link to :" << templatePath;
if (templatePath[0] != '/') {
if (templatePath.left(6) == "file:/") // I doubt this will happen
templatePath = templatePath.right(templatePath.length() - 6);
//else
- // kDebug( 30003 ) <<"dirname=" << *it;
+ // dbgUI <<"dirname=" << *it;
templatePath = *it + templatePath;
- //kDebug( 30003 ) <<"templatePath:" << templatePath;
+ //dbgUI <<"templatePath:" << templatePath;
}
} else
continue; // Invalid
}
// The else if and the else branch are here for compat. with the old system
else if (files[i].right(4) != ".png")
// Ignore everything that is not a PNG file
continue;
else {
// Found a PNG file - the template must be here in the same dir.
icon = filePath;
QFileInfo fi(filePath);
text = fi.baseName();
templatePath = filePath; // Note that we store the .png file as the template !
// That's the way it's always been done. Then the app replaces the extension...
}
KisTemplate *t = new KisTemplate(text, description, templatePath, icon, fileName,
measureSystem, hidden);
group->add(t, false, false); // false -> we aren't a "user", false -> don't
// "touch" the group to avoid useless
// creation of dirs in .kde/blah/...
if (defaultTemplate)
m_defaultTemplate = t;
}
}
}
}
void KisTemplateTree::writeTemplate(KisTemplate *t, KisTemplateGroup *group,
const QString &localDir)
{
QString fileName;
if (t->isHidden()) {
fileName = t->fileName();
// try to remove the file
if (QFile::remove(fileName) || !QFile::exists(fileName)) {
QFile::remove(t->name());
QFile::remove(t->picture());
return;
}
}
// be sure that the template's file name is unique so we don't overwrite an other
QString const path = localDir + group->name() + '/';
QString const name = KisTemplates::trimmed(t->name());
fileName = path + name + ".desktop";
if (t->isHidden() && QFile::exists(fileName))
return;
QString fill;
while (KIO::NetAccess::exists(fileName, KIO::NetAccess::SourceSide, 0)) {
fill += '_';
fileName = path + fill + name + ".desktop";
}
KConfig _config(fileName, KConfig::SimpleConfig);
KConfigGroup config(&_config, "Desktop Entry");
config.writeEntry("Type", "Link");
config.writePathEntry("URL", t->file());
config.writeEntry("Name", t->name());
config.writeEntry("Icon", t->picture());
config.writeEntry("X-KDE-Hidden", t->isHidden());
}
diff --git a/krita/ui/KisView.cpp b/krita/ui/KisView.cpp
index 581ac961c0f..b063bf81bdf 100644
--- a/krita/ui/KisView.cpp
+++ b/krita/ui/KisView.cpp
@@ -1,1022 +1,1022 @@
/*
* Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisView.h"
#include "KisView_p.h"
#include <KoDockFactoryBase.h>
#include <KoDockRegistry.h>
#include <KoDocumentInfo.h>
#include "KoDocumentInfo.h"
#include "KoPageLayout.h"
#include <KoToolManager.h>
#include <KoIcon.h>
#include <kactioncollection.h>
#include <kglobalsettings.h>
#include <klocale.h>
#include <kstatusbar.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kurl.h>
#include <QTemporaryFile>
#include <kselectaction.h>
#include <kconfiggroup.h>
#include <kdeprintdialog.h>
#include <kmenu.h>
#include <kactioncollection.h>
#include <QMessageBox>
#include <QApplication>
#include <QDesktopWidget>
#include <QDockWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QImage>
#include <QList>
#include <QPrintDialog>
#include <QToolBar>
#include <QUrl>
#include <QStatusBar>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QMoveEvent>
#include <kis_image.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <kis_layer.h>
#include <kis_mask.h>
#include <kis_selection.h>
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_canvas_resource_provider.h"
#include "kis_config.h"
#include "KisDocument.h"
#include "kis_image_manager.h"
#include "KisMainWindow.h"
#include "kis_mimedata.h"
#include "kis_mirror_axis.h"
#include "kis_node_commands_adapter.h"
#include "kis_node_manager.h"
#include "KisPart.h"
#include "KisPrintJob.h"
#include "kis_shape_controller.h"
#include "kis_tool_freehand.h"
#include "KisUndoStackAction.h"
#include "KisViewManager.h"
#include "kis_zoom_manager.h"
#include "kis_composite_progress_proxy.h"
#include "kis_statusbar.h"
#include "kis_painting_assistants_decoration.h"
#include "kis_progress_widget.h"
#include "kis_signal_compressor.h"
#include "kis_filter_manager.h"
#include "krita/gemini/ViewModeSwitchEvent.h"
//static
QString KisView::newObjectName()
{
static int s_viewIFNumber = 0;
QString name; name.setNum(s_viewIFNumber++); name.prepend("view_");
return name;
}
bool KisView::s_firstView = true;
class Q_DECL_HIDDEN KisView::Private
{
public:
Private()
: undo(0)
, redo(0)
, viewConverter(0)
, canvasController(0)
, canvas(0)
, zoomManager(0)
, viewManager(0)
, actionCollection(0)
, paintingAssistantsDecoration(0)
, isCurrent(false)
, showFloatingMessage(true)
, floatingMessageCompressor(100, KisSignalCompressor::POSTPONE)
{
tempActiveWidget = 0;
documentDeleted = false;
}
~Private() {
if (canvasController) {
KoToolManager::instance()->removeCanvasController(canvasController);
}
delete zoomManager;
delete canvasController;
delete canvas;
delete viewConverter;
}
KisUndoStackAction *undo;
KisUndoStackAction *redo;
class StatusBarItem;
QList<StatusBarItem> statusBarItems; // Our statusbar items
bool inOperation; //in the middle of an operation (no screen refreshing)?
QPointer<KisDocument> document; // our KisDocument
QWidget *tempActiveWidget;
bool documentDeleted; // true when document gets deleted [can't use document==0
// since this only happens in ~QObject, and views
// get deleted by ~KisDocument].
KisCoordinatesConverter *viewConverter;
KisCanvasController *canvasController;
KisCanvas2 *canvas;
KisZoomManager *zoomManager;
KisViewManager *viewManager;
KisNodeSP currentNode;
KActionCollection* actionCollection;
KisPaintingAssistantsDecoration *paintingAssistantsDecoration;
bool isCurrent;
bool showFloatingMessage;
QPointer<KisFloatingMessage> savedFloatingMessage;
KisSignalCompressor floatingMessageCompressor;
// Hmm sorry for polluting the private class with such a big inner class.
// At the beginning it was a little struct :)
class StatusBarItem
{
public:
StatusBarItem() // for QValueList
: m_widget(0),
m_connected(false),
m_hidden(false) {}
StatusBarItem(QWidget * widget, int stretch, bool permanent)
: m_widget(widget),
m_stretch(stretch),
m_permanent(permanent),
m_connected(false),
m_hidden(false) {}
bool operator==(const StatusBarItem& rhs) {
return m_widget == rhs.m_widget;
}
bool operator!=(const StatusBarItem& rhs) {
return m_widget != rhs.m_widget;
}
QWidget * widget() const {
return m_widget;
}
void ensureItemShown(QStatusBar * sb) {
Q_ASSERT(m_widget);
if (!m_connected) {
if (m_permanent)
sb->addPermanentWidget(m_widget, m_stretch);
else
sb->addWidget(m_widget, m_stretch);
if(!m_hidden)
m_widget->show();
m_connected = true;
}
}
void ensureItemHidden(QStatusBar * sb) {
if (m_connected) {
m_hidden = m_widget->isHidden();
sb->removeWidget(m_widget);
m_widget->hide();
m_connected = false;
}
}
private:
QWidget * m_widget;
int m_stretch;
bool m_permanent;
bool m_connected;
bool m_hidden;
};
};
KisView::KisView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent)
: QWidget(parent)
, d(new Private)
{
Q_ASSERT(document);
connect(document, SIGNAL(titleModified(QString,bool)), this, SIGNAL(titleModified(QString,bool)));
setObjectName(newObjectName());
d->document = document;
d->actionCollection = actionCollection;
setFocusPolicy(Qt::StrongFocus);
d->undo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::UNDO);
d->redo = new KisUndoStackAction(d->document->undoStack(), KisUndoStackAction::RED0);
QStatusBar * sb = statusBar();
if (sb) { // No statusbar in e.g. konqueror
connect(d->document, SIGNAL(statusBarMessage(const QString&)),
this, SLOT(slotActionStatusText(const QString&)));
connect(d->document, SIGNAL(clearStatusBarMessage()),
this, SLOT(slotClearStatusText()));
}
d->viewConverter = new KisCoordinatesConverter();
KisConfig cfg;
d->canvasController = new KisCanvasController(this, d->actionCollection);
d->canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->canvasController->setDrawShadow(false);
d->canvasController->setCanvasMode(KoCanvasController::Infinite);
d->canvasController->setVastScrolling(cfg.vastScrolling());
KConfigGroup grp(KGlobal::config(), "krita/crashprevention");
if (grp.readEntry("CreatingCanvas", false)) {
cfg.setUseOpenGL(false);
}
if (cfg.canvasState() == "OPENGL_FAILED") {
cfg.setUseOpenGL(false);
}
grp.writeEntry("CreatingCanvas", true);
grp.sync();
d->canvas = new KisCanvas2(d->viewConverter, resourceManager, this, document->shapeController());
grp.writeEntry("CreatingCanvas", false);
grp.sync();
d->canvasController->setCanvas(d->canvas);
Q_ASSERT(d->canvasController);
d->zoomManager = new KisZoomManager(this, d->viewConverter, d->canvasController);
d->zoomManager->setup(d->actionCollection);
connect(d->canvasController, SIGNAL(documentSizeChanged()), d->zoomManager, SLOT(slotScrollAreaSizeChanged()));
setAcceptDrops(true);
connect(d->document, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished()));
connect(d->document, SIGNAL(sigSavingFinished()), this, SLOT(slotSavingFinished()));
d->paintingAssistantsDecoration = new KisPaintingAssistantsDecoration(this);
d->canvas->addDecoration(d->paintingAssistantsDecoration);
d->showFloatingMessage = cfg.showCanvasMessages();
}
KisView::~KisView()
{
if (d->viewManager->filterManager()->isStrokeRunning()) {
d->viewManager->filterManager()->cancel();
}
KisPart::instance()->removeView(this);
delete d;
}
void KisView::notifyCurrentStateChanged(bool isCurrent)
{
d->isCurrent = isCurrent;
if (!d->isCurrent && d->savedFloatingMessage) {
d->savedFloatingMessage->removeMessage();
}
}
void KisView::setShowFloatingMessage(bool show)
{
d->showFloatingMessage = show;
}
void KisView::showFloatingMessageImpl(const QString message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
{
if (!d->viewManager) return;
if(d->isCurrent && d->showFloatingMessage && d->viewManager->qtMainWindow()) {
if (d->savedFloatingMessage) {
d->savedFloatingMessage->tryOverrideMessage(message, icon, timeout, priority, alignment);
} else {
d->savedFloatingMessage = new KisFloatingMessage(message, this->canvasBase()->canvasWidget(), false, timeout, priority, alignment);
d->savedFloatingMessage->setShowOverParent(true);
d->savedFloatingMessage->setIcon(icon);
connect(&d->floatingMessageCompressor, SIGNAL(timeout()), d->savedFloatingMessage, SLOT(showMessage()));
d->floatingMessageCompressor.start();
}
}
}
void KisView::setViewManager(KisViewManager *view)
{
d->viewManager = view;
connect(canvasController(), SIGNAL(toolOptionWidgetsChanged(QList<QPointer<QWidget> >)), d->viewManager->mainWindow(), SLOT(newOptionWidgets(QList<QPointer<QWidget> >)));
KoToolManager::instance()->addController(d->canvasController);
KoToolManager::instance()->registerTools(d->actionCollection, d->canvasController);
dynamic_cast<KisShapeController*>(d->document->shapeController())->setInitialShapeForCanvas(d->canvas);
if (resourceProvider()) {
resourceProvider()->slotImageSizeChanged();
}
if (d->viewManager && d->viewManager->nodeManager()) {
d->viewManager->nodeManager()->nodesUpdated();
}
connect(image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), this, SLOT(slotImageSizeChanged(const QPointF&, const QPointF&)));
connect(image(), SIGNAL(sigResolutionChanged(double,double)), this, SLOT(slotImageResolutionChanged()));
/*
* WARNING: Currently we access the global progress bar in two ways:
* connecting to composite progress proxy (strokes) and creating
* progress updaters. The latter way should be deprecated in favour
* of displaying the status of the global strokes queue
*/
image()->compositeProgressProxy()->addProxy(d->viewManager->statusBar()->progress()->progressProxy());
connect(d->viewManager->statusBar()->progress(), SIGNAL(sigCancellationRequested()), image(), SLOT(requestStrokeCancellation()));
d->viewManager->updateGUI();
KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
}
KisViewManager* KisView::viewManager() const
{
return d->viewManager;
}
KAction *KisView::undoAction() const
{
return d->undo;
}
KAction *KisView::redoAction() const
{
return d->redo;
}
KoZoomController *KisView::zoomController() const
{
return d->zoomManager->zoomController();
}
KisZoomManager *KisView::zoomManager() const
{
return d->zoomManager;
}
KisCanvasController *KisView::canvasController() const
{
return d->canvasController;
}
KisCanvasResourceProvider *KisView::resourceProvider() const
{
if (d->viewManager) {
return d->viewManager->resourceProvider();
}
return 0;
}
KisInputManager* KisView::globalInputManager() const
{
return d->viewManager ? d->viewManager->inputManager() : 0;
}
KisCanvas2 *KisView::canvasBase() const
{
return d->canvas;
}
KisImageWSP KisView::image() const
{
if (d->document) {
return d->document->image();
}
return 0;
}
KisCoordinatesConverter *KisView::viewConverter() const
{
return d->viewConverter;
}
void KisView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasImage()
|| event->mimeData()->hasUrls()
|| event->mimeData()->hasFormat("application/x-krita-node")) {
event->accept();
// activate view if it should accept the drop
this->setFocus();
} else {
event->ignore();
}
}
void KisView::dropEvent(QDropEvent *event)
{
KisImageWSP kisimage = image();
Q_ASSERT(kisimage);
QPoint cursorPos = canvasBase()->coordinatesConverter()->widgetToImage(event->pos()).toPoint();
QRect imageBounds = kisimage->bounds();
QPoint pasteCenter;
bool forceRecenter;
if (event->keyboardModifiers() & Qt::ShiftModifier &&
imageBounds.contains(cursorPos)) {
pasteCenter = cursorPos;
forceRecenter = true;
} else {
pasteCenter = imageBounds.center();
forceRecenter = false;
}
if (event->mimeData()->hasFormat("application/x-krita-node") ||
event->mimeData()->hasImage())
{
KisShapeController *kritaShapeController =
dynamic_cast<KisShapeController*>(d->document->shapeController());
QList<KisNodeSP> nodes =
KisMimeData::loadNodes(event->mimeData(), imageBounds,
pasteCenter, forceRecenter,
kisimage, kritaShapeController);
foreach(KisNodeSP node, nodes) {
if (node) {
KisNodeCommandsAdapter adapter(viewManager());
if (!viewManager()->nodeManager()->activeLayer()) {
adapter.addNode(node, kisimage->rootLayer() , 0);
} else {
adapter.addNode(node,
viewManager()->nodeManager()->activeLayer()->parent(),
viewManager()->nodeManager()->activeLayer());
}
}
}
}
else if (event->mimeData()->hasUrls()) {
QList<QUrl> urls = event->mimeData()->urls();
if (urls.length() > 0) {
KMenu popup;
popup.setObjectName("drop_popup");
QAction *insertAsNewLayer = new KAction(i18n("Insert as New Layer"), &popup);
QAction *insertManyLayers = new KAction(i18n("Insert Many Layers"), &popup);
QAction *openInNewDocument = new KAction(i18n("Open in New Document"), &popup);
QAction *openManyDocuments = new KAction(i18n("Open Many Documents"), &popup);
QAction *cancel = new KAction(i18n("Cancel"), &popup);
popup.addAction(insertAsNewLayer);
popup.addAction(openInNewDocument);
popup.addAction(insertManyLayers);
popup.addAction(openManyDocuments);
insertAsNewLayer->setEnabled(image() && urls.count() == 1);
openInNewDocument->setEnabled(urls.count() == 1);
insertManyLayers->setEnabled(image() && urls.count() > 1);
openManyDocuments->setEnabled(urls.count() > 1);
popup.addSeparator();
popup.addAction(cancel);
QAction *action = popup.exec(QCursor::pos());
if (action != 0 && action != cancel) {
foreach(const QUrl &url, urls) {
if (action == insertAsNewLayer || action == insertManyLayers) {
d->viewManager->imageManager()->importImage(KUrl(url));
activateWindow();
}
else {
Q_ASSERT(action == openInNewDocument || action == openManyDocuments);
if (mainWindow()) {
mainWindow()->openDocument(url);
}
}
}
}
}
}
}
KisDocument *KisView::document() const
{
return d->document;
}
void KisView::setDocument(KisDocument *document)
{
d->document->disconnect(this);
d->document = document;
QStatusBar *sb = statusBar();
if (sb) { // No statusbar in e.g. konqueror
connect(d->document, SIGNAL(statusBarMessage(const QString&)),
this, SLOT(slotActionStatusText(const QString&)));
connect(d->document, SIGNAL(clearStatusBarMessage()),
this, SLOT(slotClearStatusText()));
}
}
void KisView::setDocumentDeleted()
{
d->documentDeleted = true;
}
void KisView::addStatusBarItem(QWidget * widget, int stretch, bool permanent)
{
Private::StatusBarItem item(widget, stretch, permanent);
QStatusBar * sb = statusBar();
if (sb) {
item.ensureItemShown(sb);
}
d->statusBarItems.append(item);
}
void KisView::removeStatusBarItem(QWidget *widget)
{
QStatusBar *sb = statusBar();
int itemCount = d->statusBarItems.count();
for (int i = itemCount-1; i >= 0; --i) {
Private::StatusBarItem &sbItem = d->statusBarItems[i];
if (sbItem.widget() == widget) {
if (sb) {
sbItem.ensureItemHidden(sb);
}
d->statusBarItems.removeOne(sbItem);
break;
}
}
}
KoPageLayout KisView::pageLayout() const
{
return document()->pageLayout();
}
QPrintDialog *KisView::createPrintDialog(KisPrintJob *printJob, QWidget *parent)
{
QPrintDialog *printDialog = KdePrint::createPrintDialog(&printJob->printer(),
printJob->createOptionWidgets(), parent);
printDialog->setMinMax(printJob->printer().fromPage(), printJob->printer().toPage());
printDialog->setEnabledOptions(printJob->printDialogOptions());
return printDialog;
}
KisMainWindow * KisView::mainWindow() const
{
return dynamic_cast<KisMainWindow *>(window());
}
QStatusBar * KisView::statusBar() const
{
KisMainWindow *mw = mainWindow();
return mw ? mw->statusBar() : 0;
}
void KisView::slotActionStatusText(const QString &text)
{
QStatusBar *sb = statusBar();
if (sb)
sb->showMessage(text);
}
void KisView::slotClearStatusText()
{
QStatusBar *sb = statusBar();
if (sb)
sb->clearMessage();
}
QList<QAction*> KisView::createChangeUnitActions(bool addPixelUnit)
{
UnitActionGroup* unitActions = new UnitActionGroup(d->document, addPixelUnit, this);
return unitActions->actions();
}
bool KisView::event(QEvent *event)
{
switch(static_cast<int>(event->type()))
{
case ViewModeSwitchEvent::AboutToSwitchViewModeEvent: {
ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
d->canvasController->setFocus();
qApp->processEvents();
KisCanvasResourceProvider* provider = resourceProvider();
syncObject->backgroundColor = provider->bgColor();
syncObject->foregroundColor = provider->fgColor();
syncObject->exposure = provider->HDRExposure();
syncObject->gamma = provider->HDRGamma();
syncObject->compositeOp = provider->currentCompositeOp();
syncObject->pattern = provider->currentPattern();
syncObject->gradient = provider->currentGradient();
syncObject->node = provider->currentNode();
syncObject->paintOp = provider->currentPreset();
syncObject->opacity = provider->opacity();
syncObject->globalAlphaLock = provider->globalAlphaLock();
syncObject->documentOffset = d->canvasController->scrollBarValue() - pos();
syncObject->zoomLevel = zoomController()->zoomAction()->effectiveZoom();
syncObject->rotationAngle = canvasBase()->rotationAngle();
syncObject->activeToolId = KoToolManager::instance()->activeToolId();
syncObject->gridData = &document()->gridData();
syncObject->mirrorHorizontal = provider->mirrorHorizontal();
syncObject->mirrorVertical = provider->mirrorVertical();
syncObject->mirrorAxesCenter = provider->resourceManager()->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF();
KisToolFreehand* tool = qobject_cast<KisToolFreehand*>(KoToolManager::instance()->toolById(canvasBase(), syncObject->activeToolId));
if(tool) {
syncObject->smoothingOptions = tool->smoothingOptions();
}
syncObject->initialized = true;
QMainWindow* mainWindow = qobject_cast<QMainWindow*>(qApp->activeWindow());
if(mainWindow) {
QList<QDockWidget*> dockWidgets = mainWindow->findChildren<QDockWidget*>();
foreach(QDockWidget* widget, dockWidgets) {
if (widget->isFloating()) {
widget->hide();
}
}
}
return true;
}
case ViewModeSwitchEvent::SwitchedToDesktopModeEvent: {
ViewModeSynchronisationObject* syncObject = static_cast<ViewModeSwitchEvent*>(event)->synchronisationObject();
d->canvasController->setFocus();
qApp->processEvents();
if(syncObject->initialized) {
KisCanvasResourceProvider* provider = resourceProvider();
provider->resourceManager()->setResource(KisCanvasResourceProvider::MirrorAxesCenter, syncObject->mirrorAxesCenter);
if (provider->mirrorHorizontal() != syncObject->mirrorHorizontal) {
QAction* mirrorAction = d->actionCollection->action("hmirror_action");
mirrorAction->setChecked(syncObject->mirrorHorizontal);
provider->setMirrorHorizontal(syncObject->mirrorHorizontal);
}
if (provider->mirrorVertical() != syncObject->mirrorVertical) {
QAction* mirrorAction = d->actionCollection->action("vmirror_action");
mirrorAction->setChecked(syncObject->mirrorVertical);
provider->setMirrorVertical(syncObject->mirrorVertical);
}
provider->setPaintOpPreset(syncObject->paintOp);
qApp->processEvents();
KoToolManager::instance()->switchToolRequested(syncObject->activeToolId);
qApp->processEvents();
KisPaintOpPresetSP preset = canvasBase()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
preset->settings()->setProperty("CompositeOp", syncObject->compositeOp);
if(preset->settings()->hasProperty("OpacityValue"))
preset->settings()->setProperty("OpacityValue", syncObject->opacity);
provider->setPaintOpPreset(preset);
provider->setBGColor(syncObject->backgroundColor);
provider->setFGColor(syncObject->foregroundColor);
provider->setHDRExposure(syncObject->exposure);
provider->setHDRGamma(syncObject->gamma);
provider->slotPatternActivated(syncObject->pattern);
provider->slotGradientActivated(syncObject->gradient);
provider->slotNodeActivated(syncObject->node);
provider->setOpacity(syncObject->opacity);
provider->setGlobalAlphaLock(syncObject->globalAlphaLock);
provider->setCurrentCompositeOp(syncObject->compositeOp);
document()->gridData().setGrid(syncObject->gridData->gridX(), syncObject->gridData->gridY());
document()->gridData().setGridColor(syncObject->gridData->gridColor());
document()->gridData().setPaintGridInBackground(syncObject->gridData->paintGridInBackground());
document()->gridData().setShowGrid(syncObject->gridData->showGrid());
document()->gridData().setSnapToGrid(syncObject->gridData->snapToGrid());
d->actionCollection->action("zoom_in")->trigger();
qApp->processEvents();
QMainWindow* mainWindow = qobject_cast<QMainWindow*>(qApp->activeWindow());
if(mainWindow) {
QList<QDockWidget*> dockWidgets = mainWindow->findChildren<QDockWidget*>();
foreach(QDockWidget* widget, dockWidgets) {
if (widget->isFloating()) {
widget->show();
}
}
}
zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, syncObject->zoomLevel);
d->canvasController->rotateCanvas(syncObject->rotationAngle - canvasBase()->rotationAngle());
QPoint newOffset = syncObject->documentOffset + pos();
qApp->processEvents();
d->canvasController->setScrollBarValue(newOffset);
KisToolFreehand* tool = qobject_cast<KisToolFreehand*>(KoToolManager::instance()->toolById(canvasBase(), syncObject->activeToolId));
if(tool && syncObject->smoothingOptions) {
tool->smoothingOptions()->setSmoothingType(syncObject->smoothingOptions->smoothingType());
tool->smoothingOptions()->setSmoothPressure(syncObject->smoothingOptions->smoothPressure());
tool->smoothingOptions()->setTailAggressiveness(syncObject->smoothingOptions->tailAggressiveness());
tool->smoothingOptions()->setUseScalableDistance(syncObject->smoothingOptions->useScalableDistance());
tool->smoothingOptions()->setSmoothnessDistance(syncObject->smoothingOptions->smoothnessDistance());
tool->smoothingOptions()->setUseDelayDistance(syncObject->smoothingOptions->useDelayDistance());
tool->smoothingOptions()->setDelayDistance(syncObject->smoothingOptions->delayDistance());
tool->smoothingOptions()->setFinishStabilizedCurve(syncObject->smoothingOptions->finishStabilizedCurve());
tool->smoothingOptions()->setStabilizeSensors(syncObject->smoothingOptions->stabilizeSensors());
tool->updateSettingsViews();
}
}
return true;
}
default:
break;
}
return QWidget::event( event );
}
void KisView::closeEvent(QCloseEvent *event)
{
// Check whether we're the last view
int viewCount = KisPart::instance()->viewCount(document());
if (viewCount > 1) {
// there are others still, so don't bother the user
event->accept();
return;
}
if (queryClose()) {
d->viewManager->removeStatusBarItem(zoomManager()->zoomActionWidget());
event->accept();
return;
}
event->ignore();
}
bool KisView::queryClose()
{
if (!document())
return true;
if (document()->isModified()) {
QString name;
if (document()->documentInfo()) {
name = document()->documentInfo()->aboutInfo("title");
}
if (name.isEmpty())
name = document()->url().fileName();
if (name.isEmpty())
name = i18n("Untitled");
int res = QMessageBox::warning(this,
i18nc("@title:window", "Krita"),
i18n("<p>The document <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", name),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
switch (res) {
case QMessageBox::Yes : {
bool isNative = (document()->outputMimeType() == document()->nativeFormatMimeType());
if (!viewManager()->mainWindow()->saveDocument(document(), !isNative))
return false;
break;
}
case QMessageBox::No :
document()->removeAutoSaveFiles();
document()->setModified(false); // Now when queryClose() is called by closeEvent it won't do anything.
break;
default : // case QMessageBox::Cancel :
return false;
}
}
return true;
}
void KisView::resetImageSizeAndScroll(bool changeCentering,
const QPointF oldImageStillPoint,
const QPointF newImageStillPoint)
{
const KisCoordinatesConverter *converter = d->canvas->coordinatesConverter();
QPointF oldPreferredCenter = d->canvasController->preferredCenter();
/**
* Calculating the still point in old coordinates depending on the
* parameters given
*/
QPointF oldStillPoint;
if (changeCentering) {
oldStillPoint =
converter->imageToWidget(oldImageStillPoint) +
converter->documentOffset();
} else {
QSize oldDocumentSize = d->canvasController->documentSize();
oldStillPoint = QPointF(0.5 * oldDocumentSize.width(), 0.5 * oldDocumentSize.height());
}
/**
* Updating the document size
*/
QSizeF size(image()->width() / image()->xRes(), image()->height() / image()->yRes());
KoZoomController *zc = d->zoomManager->zoomController();
zc->setZoom(KoZoomMode::ZOOM_CONSTANT, zc->zoomAction()->effectiveZoom());
zc->setPageSize(size);
zc->setDocumentSize(size, true);
/**
* Calculating the still point in new coordinates depending on the
* parameters given
*/
QPointF newStillPoint;
if (changeCentering) {
newStillPoint =
converter->imageToWidget(newImageStillPoint) +
converter->documentOffset();
} else {
QSize newDocumentSize = d->canvasController->documentSize();
newStillPoint = QPointF(0.5 * newDocumentSize.width(), 0.5 * newDocumentSize.height());
}
d->canvasController->setPreferredCenter(oldPreferredCenter - oldStillPoint + newStillPoint);
}
void KisView::setCurrentNode(KisNodeSP node)
{
d->currentNode = node;
}
KisNodeSP KisView::currentNode() const
{
return d->currentNode;
}
KisLayerSP KisView::currentLayer() const
{
KisNodeSP node;
KisMaskSP mask = currentMask();
if (mask) {
node = mask->parent();
}
else {
node = d->currentNode;
}
return dynamic_cast<KisLayer*>(node.data());
}
KisMaskSP KisView::currentMask() const
{
return dynamic_cast<KisMask*>(d->currentNode.data());
}
KisSelectionSP KisView::selection()
{
KisLayerSP layer = currentLayer();
if (layer)
return layer->selection(); // falls through to the global
// selection, or 0 in the end
if (image()) {
return image()->globalSelection();
}
return 0;
}
void KisView::slotLoadingFinished()
{
if (!document()) return;
/**
* Cold-start of image size/resolution signals
*/
slotImageResolutionChanged();
if (image()->locked()) {
// If this is the first view on the image, the image will have been locked
// so unlock it.
image()->blockSignals(false);
image()->unlock();
}
if (d->paintingAssistantsDecoration){
foreach(KisPaintingAssistant* assist, document()->preLoadedAssistants()){
d->paintingAssistantsDecoration->addAssistant(assist);
}
d->paintingAssistantsDecoration->setVisible(true);
}
canvasBase()->initializeImage();
/**
* Dirty hack alert
*/
d->zoomManager->zoomController()->setAspectMode(true);
if (viewConverter()) {
viewConverter()->setZoomMode(KoZoomMode::ZOOM_PAGE);
}
connect(image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)));
connect(image(), SIGNAL(sigProfileChanged(const KoColorProfile*)), this, SIGNAL(sigProfileChanged(const KoColorProfile*)));
connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SIGNAL(sigSizeChanged(QPointF,QPointF)));
KisNodeSP activeNode = document()->preActivatedNode();
document()->setPreActivatedNode(0); // to make sure that we don't keep a reference to a layer the user can later delete.
if (!activeNode) {
activeNode = image()->rootLayer()->lastChild();
}
while (activeNode && !activeNode->inherits("KisLayer")) {
activeNode = activeNode->prevSibling();
}
setCurrentNode(activeNode);
}
void KisView::slotSavingFinished()
{
if (d->viewManager && d->viewManager->mainWindow()) {
d->viewManager->mainWindow()->updateCaption();
}
}
KisPrintJob * KisView::createPrintJob()
{
return new KisPrintJob(image());
}
void KisView::slotImageResolutionChanged()
{
resetImageSizeAndScroll(false);
zoomManager()->updateGUI();
// update KoUnit value for the document
if (resourceProvider()) {
resourceProvider()->resourceManager()->
setResource(KoCanvasResourceManager::Unit, d->canvas->unit());
}
}
void KisView::slotImageSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint)
{
resetImageSizeAndScroll(true, oldStillPoint, newStillPoint);
zoomManager()->updateGUI();
}
diff --git a/krita/ui/KisViewManager.cpp b/krita/ui/KisViewManager.cpp
index e392f296837..3cea299e142 100644
--- a/krita/ui/KisViewManager.cpp
+++ b/krita/ui/KisViewManager.cpp
@@ -1,1346 +1,1346 @@
/*
* This file is part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* 1999 Michael Koch <koch@kde.org>
* 1999 Carsten Pfeiffer <pfeiffer@kde.org>
* 2002 Patrick Julien <freak@codepimps.org>
* 2003-2011 Boudewijn Rempt <boud@valdyas.org>
* 2004 Clarence Dang <dang@kde.org>
* 2011 José Luis Vergara <pentalis@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include "KisViewManager.h"
#include <QPrinter>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QGridLayout>
#include <QRect>
#include <QWidget>
#include <QToolBar>
#include <QApplication>
#include <QPrintDialog>
#include <QObject>
#include <QByteArray>
#include <QBuffer>
#include <QScrollBar>
#include <QMainWindow>
#include <QPoint>
#include <kactioncollection.h>
#include <kaction.h>
#include <klocale.h>
#include <kmenubar.h>
#include <kmenu.h>
#include <QMessageBox>
#include <kservice.h>
#include <kstandarddirs.h>
#include <kstatusbar.h>
#include <kurl.h>
#include <kselectaction.h>
#include <kxmlguifactory.h>
#include <KoCanvasController.h>
#include <KoCompositeOp.h>
#include <KoDockRegistry.h>
#include <KoDockWidgetTitleBar.h>
#include <KoProperties.h>
#include <KoResourceItemChooserSync.h>
#include <KoSelection.h>
#include <KoStore.h>
#include <KoToolManager.h>
#include <KoToolRegistry.h>
#include <KoViewConverter.h>
#include <KoZoomHandler.h>
#include <KoPluginLoader.h>
#include <KoDocumentInfo.h>
#include <KoGlobal.h>
#include "input/kis_input_manager.h"
#include "canvas/kis_canvas2.h"
#include "canvas/kis_canvas_controller.h"
#include "canvas/kis_grid_manager.h"
#include "canvas/kis_perspective_grid_manager.h"
#include "dialogs/kis_dlg_blacklist_cleanup.h"
#include "input/kis_input_profile_manager.h"
#include "kis_action_manager.h"
#include "kis_action.h"
#include "kis_canvas_controls_manager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_composite_progress_proxy.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_control_frame.h"
#include "kis_coordinates_converter.h"
#include <KisDocumentEntry.h>
#include "KisDocument.h"
#include "kis_factory2.h"
#include "kis_favorite_resource_manager.h"
#include "kis_filter_manager.h"
#include "kis_group_layer.h"
#include <kis_image.h>
#include "kis_image_manager.h"
#include <kis_layer.h>
#include "KisMainWindow.h"
#include "kis_mainwindow_observer.h"
#include "kis_mask_manager.h"
#include "kis_mimedata.h"
#include "kis_mirror_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_node.h"
#include "kis_node_manager.h"
#include "kis_painting_assistants_manager.h"
#include <kis_paint_layer.h>
#include "kis_paintop_box.h"
#include <kis_paintop_preset.h>
#include "KisPart.h"
#include "KisPrintJob.h"
#include "kis_progress_widget.h"
#include "kis_resource_server_provider.h"
#include "kis_selection.h"
#include "kis_selection_manager.h"
#include "kis_shape_controller.h"
#include "kis_shape_layer.h"
#include <kis_signal_compressor.h>
#include "kis_statusbar.h"
#include <KisTemplateCreateDia.h>
#include <kis_tool_freehand.h>
#include "kis_tooltip_manager.h"
#include <kis_undo_adapter.h>
#include "KisView.h"
#include "kis_zoom_manager.h"
#include "kra/kis_kra_loader.h"
#include "widgets/kis_floating_message.h"
#include "kis_signal_auto_connection.h"
#include "kis_icon_utils.h"
class StatusBarItem
{
public:
StatusBarItem() // for QValueList
: m_widget(0),
m_connected(false),
m_hidden(false) {}
StatusBarItem(QWidget * widget, int stretch, bool permanent)
: m_widget(widget),
m_stretch(stretch),
m_permanent(permanent),
m_connected(false),
m_hidden(false) {}
bool operator==(const StatusBarItem& rhs) {
return m_widget == rhs.m_widget;
}
bool operator!=(const StatusBarItem& rhs) {
return m_widget != rhs.m_widget;
}
QWidget * widget() const {
return m_widget;
}
void ensureItemShown(QStatusBar * sb) {
Q_ASSERT(m_widget);
if (!m_connected) {
if (m_permanent)
sb->addPermanentWidget(m_widget, m_stretch);
else
sb->addWidget(m_widget, m_stretch);
if(!m_hidden)
m_widget->show();
m_connected = true;
}
}
void ensureItemHidden(QStatusBar * sb) {
if (m_connected) {
m_hidden = m_widget->isHidden();
sb->removeWidget(m_widget);
m_widget->hide();
m_connected = false;
}
}
private:
QWidget * m_widget;
int m_stretch;
bool m_permanent;
bool m_connected;
bool m_hidden;
};
class BlockingUserInputEventFilter : public QObject
{
bool eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched);
if(dynamic_cast<QWheelEvent*>(event)
|| dynamic_cast<QKeyEvent*>(event)
|| dynamic_cast<QMouseEvent*>(event)) {
return true;
}
else {
return false;
}
}
};
class KisViewManager::KisViewManagerPrivate
{
public:
KisViewManagerPrivate()
: filterManager(0)
, statusBar(0)
, createTemplate(0)
, saveIncremental(0)
, saveIncrementalBackup(0)
, openResourcesDirectory(0)
, rotateCanvasRight(0)
, rotateCanvasLeft(0)
, wrapAroundAction(0)
, showRulersAction(0)
, zoomTo100pct(0)
, zoomIn(0)
, zoomOut(0)
, showGuidesAction(0)
, selectionManager(0)
, controlFrame(0)
, nodeManager(0)
, imageManager(0)
, gridManager(0)
, perspectiveGridManager(0)
, paintingAssistantsManager(0)
, actionManager(0)
, mainWindow(0)
, showFloatingMessage(true)
, currentImageView(0)
, canvasResourceProvider(0)
, canvasResourceManager(0)
, guiUpdateCompressor(0)
, actionCollection(0)
, mirrorManager(0)
, actionAuthor(0)
{
}
~KisViewManagerPrivate() {
delete filterManager;
delete selectionManager;
delete nodeManager;
delete imageManager;
delete gridManager;
delete perspectiveGridManager;
delete paintingAssistantsManager;
delete statusBar;
delete actionManager;
delete canvasControlsManager;
delete canvasResourceProvider;
delete canvasResourceManager;
delete mirrorManager;
}
public:
KisFilterManager *filterManager;
KisStatusBar *statusBar;
KisAction *createTemplate;
KisAction *createCopy;
KisAction *saveIncremental;
KisAction *saveIncrementalBackup;
KisAction *openResourcesDirectory;
KisAction *rotateCanvasRight;
KisAction *rotateCanvasLeft;
KisAction *wrapAroundAction;
KisAction *showRulersAction;
KisAction *zoomTo100pct;
KisAction *zoomIn;
KisAction *zoomOut;
KisAction *showGuidesAction;
KisSelectionManager *selectionManager;
KisControlFrame *controlFrame;
KisNodeManager *nodeManager;
KisImageManager *imageManager;
KisGridManager *gridManager;
KisCanvasControlsManager *canvasControlsManager;
KisPerspectiveGridManager * perspectiveGridManager;
KisPaintingAssistantsManager *paintingAssistantsManager;
BlockingUserInputEventFilter blockingEventFilter;
KisActionManager* actionManager;
QMainWindow* mainWindow;
QPointer<KisFloatingMessage> savedFloatingMessage;
bool showFloatingMessage;
QPointer<KisView> currentImageView;
KisCanvasResourceProvider* canvasResourceProvider;
KoCanvasResourceManager* canvasResourceManager;
QList<StatusBarItem> statusBarItems;
KisSignalCompressor* guiUpdateCompressor;
KActionCollection *actionCollection;
KisMirrorManager *mirrorManager;
QPointer<KisInputManager> inputManager;
KisSignalAutoConnectionsStore viewConnections;
KSelectAction *actionAuthor; // Select action for author profile.
};
KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection)
: d(new KisViewManagerPrivate())
{
d->actionCollection = _actionCollection;
d->actionManager = new KisActionManager(this);
d->mainWindow = dynamic_cast<QMainWindow*>(parent);
d->canvasResourceProvider = new KisCanvasResourceProvider(this);
d->canvasResourceManager = new KoCanvasResourceManager();
d->canvasResourceProvider->setResourceManager(d->canvasResourceManager);
d->guiUpdateCompressor = new KisSignalCompressor(30, KisSignalCompressor::POSTPONE, this);
connect(d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
createActions();
createManagers();
d->controlFrame = new KisControlFrame(this, parent);
//Check to draw scrollbars after "Canvas only mode" toggle is created.
this->showHideScrollbars();
KoCanvasController *dummy = new KoDummyCanvasController(actionCollection());
KoToolManager::instance()->registerTools(actionCollection(), dummy);
d->statusBar = new KisStatusBar(this);
QTimer::singleShot(0, this, SLOT(makeStatusBarVisible()));
connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
d->controlFrame->paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
d->controlFrame->paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int)));
connect(d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
resourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
d->controlFrame->paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant)));
connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
KisInputProfileManager::instance()->loadProfiles();
KisConfig cfg;
d->showFloatingMessage = cfg.showCanvasMessages();
}
KisViewManager::~KisViewManager()
{
KisConfig cfg;
if (resourceProvider() && resourceProvider()->currentPreset()) {
cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name());
}
cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength());
delete d;
}
KActionCollection *KisViewManager::actionCollection() const
{
return d->actionCollection;
}
void KisViewManager::slotViewAdded(KisView *view)
{
d->inputManager->addTrackedCanvas(view->canvasBase());
}
void KisViewManager::slotViewRemoved(KisView *view)
{
d->inputManager->removeTrackedCanvas(view->canvasBase());
}
void KisViewManager::setCurrentView(KisView *view)
{
bool first = true;
if (d->currentImageView) {
d->currentImageView->notifyCurrentStateChanged(false);
d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
first = false;
KisDocument* doc = d->currentImageView->document();
if (doc) {
doc->disconnect(this);
}
d->currentImageView->canvasController()->proxyObject->disconnect(d->statusBar);
d->viewConnections.clear();
}
QPointer<KisView>imageView = qobject_cast<KisView*>(view);
if (imageView) {
// Wait for the async image to have loaded
KisDocument* doc = view->document();
// connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF)));
d->currentImageView = imageView;
KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
d->viewConnections.addUniqueConnection(d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEnd()));
d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
d->viewConnections.addUniqueConnection(d->currentImageView->canvasController(), SIGNAL(toolOptionWidgetsChanged(QList<QPointer<QWidget> >)), mainWindow(), SLOT(newOptionWidgets(QList<QPointer<QWidget> >)));
d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame->paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(toggleShowRulers(bool)));
d->showRulersAction->setChecked(imageView->zoomManager()->horizontalRulerVisible() && imageView->zoomManager()->verticalRulerVisible());
d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn()));
d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut()));
d->viewConnections.addUniqueConnection(d->showGuidesAction, SIGNAL(triggered(bool)), imageView->zoomManager(), SLOT(showGuides(bool)));
showHideScrollbars();
}
d->filterManager->setView(imageView);
d->selectionManager->setView(imageView);
d->nodeManager->setView(imageView);
d->imageManager->setView(imageView);
d->canvasControlsManager->setView(imageView);
d->actionManager->setView(imageView);
d->gridManager->setView(imageView);
d->statusBar->setView(imageView);
d->paintingAssistantsManager->setView(imageView);
d->perspectiveGridManager->setView(imageView);
d->mirrorManager->setView(imageView);
if (d->currentImageView) {
d->currentImageView->notifyCurrentStateChanged(true);
d->currentImageView->canvasController()->activate();
d->currentImageView->canvasController()->setFocus();
}
d->actionManager->updateGUI();
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)),
resourceProvider(), SLOT(slotImageSizeChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigResolutionChanged(double,double)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(updateGUI()));
d->viewConnections.addUniqueConnection(
view->zoomManager()->zoomController(),
SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
resourceProvider()->slotImageSizeChanged();
resourceProvider()->slotOnScreenResolutionChanged();
// Restore the last used brush preset
if (first) {
KisConfig cfg;
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default"));
KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset);
if (!preset) {
preset = rserver->resourceByName("Basic_tip_default");
}
if (!preset) {
preset = rserver->resources().first();
}
if (preset) {
paintOpBox()->restoreResource(preset.data());
}
}
}
KoZoomController *KisViewManager::zoomController() const
{
if (d->currentImageView) {
return d->currentImageView->zoomController();
}
return 0;
}
KisImageWSP KisViewManager::image() const
{
if (document()) {
return document()->image();
}
return 0;
}
KisCanvasResourceProvider * KisViewManager::resourceProvider()
{
return d->canvasResourceProvider;
}
KisCanvas2 * KisViewManager::canvasBase() const
{
if (d && d->currentImageView) {
return d->currentImageView->canvasBase();
}
return 0;
}
QWidget* KisViewManager::canvas() const
{
if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
return d->currentImageView->canvasBase()->canvasWidget();
}
return 0;
}
KisStatusBar * KisViewManager::statusBar() const
{
return d->statusBar;
}
void KisViewManager::addStatusBarItem(QWidget * widget, int stretch, bool permanent)
{
if (!mainWindow()) return;
StatusBarItem item(widget, stretch, permanent);
QStatusBar * sb = mainWindow()->statusBar();
if (sb) {
item.ensureItemShown(sb);
}
d->statusBarItems.append(item);
}
void KisViewManager::removeStatusBarItem(QWidget * widget)
{
QStatusBar *sb = mainWindow()->statusBar();
int itemCount = d->statusBarItems.count();
for (int i = itemCount-1; i >= 0; --i) {
StatusBarItem &sbItem = d->statusBarItems[i];
if (sbItem.widget() == widget) {
if (sb) {
sbItem.ensureItemHidden(sb);
}
d->statusBarItems.removeOne(sbItem);
break;
}
}
}
KisPaintopBox* KisViewManager::paintOpBox() const
{
return d->controlFrame->paintopBox();
}
KoProgressUpdater* KisViewManager::createProgressUpdater(KoProgressUpdater::Mode mode)
{
return new KisProgressUpdater(d->statusBar->progress(), document()->progressProxy(), mode);
}
KisSelectionManager * KisViewManager::selectionManager()
{
return d->selectionManager;
}
KisNodeSP KisViewManager::activeNode()
{
if (d->nodeManager)
return d->nodeManager->activeNode();
else
return 0;
}
KisLayerSP KisViewManager::activeLayer()
{
if (d->nodeManager)
return d->nodeManager->activeLayer();
else
return 0;
}
KisPaintDeviceSP KisViewManager::activeDevice()
{
if (d->nodeManager)
return d->nodeManager->activePaintDevice();
else
return 0;
}
KisZoomManager * KisViewManager::zoomManager()
{
if (d->currentImageView) {
return d->currentImageView->zoomManager();
}
return 0;
}
KisFilterManager * KisViewManager::filterManager()
{
return d->filterManager;
}
KisImageManager * KisViewManager::imageManager()
{
return d->imageManager;
}
KisInputManager* KisViewManager::inputManager() const
{
return d->inputManager;
}
KisSelectionSP KisViewManager::selection()
{
if (d->currentImageView) {
return d->currentImageView->selection();
}
return 0;
}
bool KisViewManager::selectionEditable()
{
KisLayerSP layer = activeLayer();
if (layer) {
KoProperties properties;
QList<KisNodeSP> masks = layer->childNodes(QStringList("KisSelectionMask"), properties);
if (masks.size() == 1) {
return masks[0]->isEditable();
}
}
// global selection is always editable
return true;
}
KisUndoAdapter * KisViewManager::undoAdapter()
{
if (!document()) return 0;
KisImageWSP image = document()->image();
Q_ASSERT(image);
return image->undoAdapter();
}
void KisViewManager::createActions()
{
d->saveIncremental = new KisAction(i18n("Save Incremental &Version"), this);
d->saveIncremental->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_S));
d->saveIncremental->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("save_incremental_version", d->saveIncremental);
connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
d->saveIncrementalBackup = new KisAction(i18n("Save Incremental Backup"), this);
d->saveIncrementalBackup->setShortcut(Qt::Key_F4);
d->saveIncrementalBackup->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("save_incremental_backup", d->saveIncrementalBackup);
connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
d->saveIncremental->setEnabled(false);
d->saveIncrementalBackup->setEnabled(false);
KisAction *tabletDebugger = new KisAction(i18n("Toggle Tablet Debugger"), this);
actionManager()->addAction("tablet_debugger", tabletDebugger );
tabletDebugger->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T));
connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
d->createTemplate = new KisAction( i18n( "&Create Template From Image..." ), this);
d->createTemplate->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("create_template", d->createTemplate);
connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
d->createCopy = new KisAction( i18n( "&Create Copy From Current Image" ), this);
d->createCopy->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("create_copy", d->createCopy);
connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
d->openResourcesDirectory = new KisAction(i18n("Open Resources Folder"), this);
d->openResourcesDirectory->setToolTip(i18n("Opens a file browser at the location Krita saves resources such as brushes to."));
d->openResourcesDirectory->setWhatsThis(i18n("Opens a file browser at the location Krita saves resources such as brushes to."));
actionManager()->addAction("open_resources_directory", d->openResourcesDirectory);
connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
d->rotateCanvasRight = new KisAction(i18n("Rotate Canvas Right"), this);
actionManager()->addAction("rotate_canvas_right", d->rotateCanvasRight);
d->rotateCanvasRight->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->rotateCanvasRight->setShortcut(QKeySequence("Ctrl+]"));
d->rotateCanvasLeft = new KisAction(i18n("Rotate Canvas Left"), this);
actionManager()->addAction("rotate_canvas_left", d->rotateCanvasLeft);
d->rotateCanvasLeft->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->rotateCanvasLeft->setShortcut(QKeySequence("Ctrl+["));
d->wrapAroundAction = new KisAction(i18n("Wrap Around Mode"), this);
d->wrapAroundAction->setCheckable(true);
d->wrapAroundAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("wrap_around_mode", d->wrapAroundAction);
d->wrapAroundAction->setShortcut(QKeySequence(Qt::Key_W));
KisAction *tAction = new KisAction(i18n("Show Status Bar"), this);
tAction->setCheckable(true);
tAction->setChecked(true);
tAction->setToolTip(i18n("Shows or hides the status bar"));
actionManager()->addAction("showStatusBar", tAction);
tAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
tAction = new KisAction(i18n("Show Canvas Only"), this);
tAction->setActivationFlags(KisAction::NONE);
tAction->setCheckable(true);
tAction->setToolTip(i18n("Shows just the canvas or the whole window"));
QList<QKeySequence> shortcuts;
shortcuts << QKeySequence(Qt::Key_Tab);
tAction->setShortcuts(shortcuts);
tAction->setChecked(false);
actionManager()->addAction("view_show_just_the_canvas", tAction);
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showJustTheCanvas(bool)));
//Workaround, by default has the same shortcut as mirrorCanvas
KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
if (a) {
a->setShortcut(QKeySequence(), KAction::DefaultShortcut);
a->setShortcut(QKeySequence(), KAction::ActiveShortcut);
a->setActivationConditions(KisAction::SELECTION_EDITABLE);
}
a = new KisAction(i18n("Cleanup removed files..."), this);
actionManager()->addAction("edit_blacklist_cleanup", a);
connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup()));
d->showRulersAction = new KisAction(i18n("Show Rulers"), this);
d->showRulersAction->setCheckable(true);
d->showRulersAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("view_ruler", d->showRulersAction);
d->showRulersAction->setWhatsThis(i18n("The rulers show the horizontal and vertical positions of the mouse on the image "
"and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p>"));
KisConfig cfg;
d->showRulersAction->setChecked(cfg.showRulers());
d->showGuidesAction = new KisAction(i18n("Show Guides"), this);
d->showGuidesAction->setCheckable(true);
d->showGuidesAction->setCheckable(false);
d->showGuidesAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->showGuidesAction->setToolTip(i18n("Shows or hides guides"));
actionManager()->addAction("view_show_guides", d->showGuidesAction);
d->zoomTo100pct = new KisAction(i18n("Reset zoom"), this);
d->zoomTo100pct->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager()->addAction("zoom_to_100pct", d->zoomTo100pct);
d->zoomTo100pct->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_0 ) );
d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, "");
d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, "");
d->actionAuthor = new KSelectAction(themedIcon("im-user"), i18n("Active Author Profile"), this);
connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &)));
actionCollection()->addAction("settings_active_author", d->actionAuthor);
slotUpdateAuthorProfileActions();
}
void KisViewManager::createManagers()
{
// Create the managers for filters, selections, layers etc.
// XXX: When the currentlayer changes, call updateGUI on all
// managers
d->filterManager = new KisFilterManager(this);
d->filterManager->setup(actionCollection(), actionManager());
d->selectionManager = new KisSelectionManager(this);
d->selectionManager->setup(actionManager());
d->nodeManager = new KisNodeManager(this);
d->nodeManager->setup(actionCollection(), actionManager());
d->imageManager = new KisImageManager(this);
d->imageManager->setup(actionManager());
d->gridManager = new KisGridManager(this);
d->gridManager->setup(this->actionManager());
d->perspectiveGridManager = new KisPerspectiveGridManager(this);
d->perspectiveGridManager->setup(actionCollection());
d->paintingAssistantsManager = new KisPaintingAssistantsManager(this);
d->paintingAssistantsManager->setup(actionManager());
d->canvasControlsManager = new KisCanvasControlsManager(this);
d->canvasControlsManager->setup(actionManager());
d->mirrorManager = new KisMirrorManager(this);
d->mirrorManager->setup(actionCollection());
d->inputManager = new KisInputManager(this);
}
void KisViewManager::updateGUI()
{
d->guiUpdateCompressor->start();
}
void KisViewManager::slotBlacklistCleanup()
{
KisDlgBlacklistCleanup dialog;
dialog.exec();
}
KisNodeManager * KisViewManager::nodeManager() const
{
return d->nodeManager;
}
KisActionManager* KisViewManager::actionManager() const
{
return d->actionManager;
}
KisPerspectiveGridManager* KisViewManager::perspectiveGridManager() const
{
return d->perspectiveGridManager;
}
KisGridManager * KisViewManager::gridManager() const
{
return d->gridManager;
}
KisPaintingAssistantsManager* KisViewManager::paintingAssistantsManager() const
{
return d->paintingAssistantsManager;
}
KisDocument *KisViewManager::document() const
{
if (d->currentImageView && d->currentImageView->document()) {
return d->currentImageView->document();
}
return 0;
}
int KisViewManager::viewCount() const
{
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
if (mw) {
return mw->viewCount();
}
return 0;
}
void KisViewManager::slotCreateTemplate()
{
if (!document()) return;
KisTemplateCreateDia::createTemplate(KisPart::instance()->templatesResourcePath(), ".kra",
KisFactory::componentData(), document(), mainWindow());
}
void KisViewManager::slotCreateCopy()
{
if (!document()) return;
KisDocument *doc = KisPart::instance()->createDocument();
QString name = document()->documentInfo()->aboutInfo("name");
if (name.isEmpty()) {
name = document()->url().toLocalFile();
}
name = i18n("%1 (Copy)", name);
doc->documentInfo()->setAboutInfo("title", name);
KisImageWSP image = document()->image();
KisImageSP newImage = new KisImage(doc->createUndoStore(), image->width(), image->height(), image->colorSpace(), name);
newImage->setRootLayer(dynamic_cast<KisGroupLayer*>(image->rootLayer()->clone().data()));
doc->setCurrentImage(newImage);
KisPart::instance()->addDocument(doc);
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
KisView *view = KisPart::instance()->createView(doc, resourceProvider()->resourceManager(), mw->actionCollection(), mw);
mw->addView(view);
}
QMainWindow* KisViewManager::qtMainWindow() const
{
if (d->mainWindow)
return d->mainWindow;
//Fallback for when we have not yet set the main window.
QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
if(w)
return w;
return mainWindow();
}
void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
{
d->mainWindow = newMainWindow;
}
void KisViewManager::slotDocumentSaved()
{
d->saveIncremental->setEnabled(true);
d->saveIncrementalBackup->setEnabled(true);
}
void KisViewManager::slotSaveIncremental()
{
if (!document()) return;
bool foundVersion;
bool fileAlreadyExists;
bool isBackup;
QString version = "000";
QString newVersion;
QString letter;
QString fileName = document()->localFilePath();
// Find current version filenames
// v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
// Considering our incremental version and backup scheme, format is filename_001~001.ext
QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regex.indexIn(fileName); // Perform the search
QStringList matches = regex.capturedTexts();
foundVersion = matches.at(0).isEmpty() ? false : true;
// Ensure compatibility with Save Incremental Backup
// If this regex is not kept separate, the entire algorithm needs modification;
// It's simpler to just add this.
QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regexAux.indexIn(fileName); // Perform the search
QStringList matchesAux = regexAux.capturedTexts();
isBackup = matchesAux.at(0).isEmpty() ? false : true;
// If the filename has a version, prepare it for incrementation
if (foundVersion) {
version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
if (version.contains(QRegExp("[a-z]"))) {
version.chop(1); // Trim "."
letter = version.right(1); // Save letter
version.chop(1); // Trim letter
} else {
version.chop(1); // Trim "."
}
version.remove(0, 1); // Trim "_"
} else {
// ...else, simply add a version to it so the next loop works
QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
regex2.indexIn(fileName);
QStringList matches2 = regex2.capturedTexts();
QString extensionPlusVersion = matches2.at(0);
extensionPlusVersion.prepend(version);
extensionPlusVersion.prepend("_");
fileName.replace(regex2, extensionPlusVersion);
}
// Prepare the base for new version filename
int intVersion = version.toInt(0);
++intVersion;
QString baseNewVersion = QString::number(intVersion);
while (baseNewVersion.length() < version.length()) {
baseNewVersion.prepend("0");
}
// Check if the file exists under the new name and search until options are exhausted (test appending a to z)
do {
newVersion = baseNewVersion;
newVersion.prepend("_");
if (!letter.isNull()) newVersion.append(letter);
if (isBackup) {
newVersion.append("~");
} else {
newVersion.append(".");
}
fileName.replace(regex, newVersion);
fileAlreadyExists = QFile(fileName).exists();
if (fileAlreadyExists) {
if (!letter.isNull()) {
char letterCh = letter.at(0).toLatin1();
++letterCh;
letter = QString(QChar(letterCh));
} else {
letter = 'a';
}
}
} while (fileAlreadyExists && letter != "{"); // x, y, z, {...
if (letter == "{") {
QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
return;
}
document()->setSaveInBatchMode(true);
document()->saveAs(fileName);
document()->setSaveInBatchMode(false);
if (mainWindow()) {
mainWindow()->updateCaption();
}
}
void KisViewManager::slotSaveIncrementalBackup()
{
if (!document()) return;
bool workingOnBackup;
bool fileAlreadyExists;
QString version = "000";
QString newVersion;
QString letter;
QString fileName = document()->localFilePath();
// First, discover if working on a backup file, or a normal file
QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
regex.indexIn(fileName); // Perform the search
QStringList matches = regex.capturedTexts();
workingOnBackup = matches.at(0).isEmpty() ? false : true;
if (workingOnBackup) {
// Try to save incremental version (of backup), use letter for alt versions
version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
if (version.contains(QRegExp("[a-z]"))) {
version.chop(1); // Trim "."
letter = version.right(1); // Save letter
version.chop(1); // Trim letter
} else {
version.chop(1); // Trim "."
}
version.remove(0, 1); // Trim "~"
// Prepare the base for new version filename
int intVersion = version.toInt(0);
++intVersion;
QString baseNewVersion = QString::number(intVersion);
QString backupFileName = document()->localFilePath();
while (baseNewVersion.length() < version.length()) {
baseNewVersion.prepend("0");
}
// Check if the file exists under the new name and search until options are exhausted (test appending a to z)
do {
newVersion = baseNewVersion;
newVersion.prepend("~");
if (!letter.isNull()) newVersion.append(letter);
newVersion.append(".");
backupFileName.replace(regex, newVersion);
fileAlreadyExists = QFile(backupFileName).exists();
if (fileAlreadyExists) {
if (!letter.isNull()) {
char letterCh = letter.at(0).toLatin1();
++letterCh;
letter = QString(QChar(letterCh));
} else {
letter = 'a';
}
}
} while (fileAlreadyExists && letter != "{"); // x, y, z, {...
if (letter == "{") {
QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
return;
}
QFile::copy(fileName, backupFileName);
document()->saveAs(fileName);
if (mainWindow()) mainWindow()->updateCaption();
}
else { // if NOT working on a backup...
// Navigate directory searching for latest backup version, ignore letters
const quint8 HARDCODED_DIGIT_COUNT = 3;
QString baseNewVersion = "000";
QString backupFileName = document()->localFilePath();
QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
regex2.indexIn(backupFileName);
QStringList matches2 = regex2.capturedTexts();
QString extensionPlusVersion = matches2.at(0);
extensionPlusVersion.prepend(baseNewVersion);
extensionPlusVersion.prepend("~");
backupFileName.replace(regex2, extensionPlusVersion);
// Save version with 1 number higher than the highest version found ignoring letters
do {
newVersion = baseNewVersion;
newVersion.prepend("~");
newVersion.append(".");
backupFileName.replace(regex, newVersion);
fileAlreadyExists = QFile(backupFileName).exists();
if (fileAlreadyExists) {
// Prepare the base for new version filename, increment by 1
int intVersion = baseNewVersion.toInt(0);
++intVersion;
baseNewVersion = QString::number(intVersion);
while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
baseNewVersion.prepend("0");
}
}
} while (fileAlreadyExists);
// Save both as backup and on current file for interapplication workflow
document()->setSaveInBatchMode(true);
QFile::copy(fileName, backupFileName);
document()->saveAs(fileName);
document()->setSaveInBatchMode(false);
if (mainWindow()) mainWindow()->updateCaption();
}
}
void KisViewManager::disableControls()
{
// prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
// this is for Bug 250944
// the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
// see KisToolFreehand::initPaint() and endPaint()
d->controlFrame->paintopBox()->installEventFilter(&d->blockingEventFilter);
foreach(QObject* child, d->controlFrame->paintopBox()->children()) {
child->installEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::enableControls()
{
d->controlFrame->paintopBox()->removeEventFilter(&d->blockingEventFilter);
foreach(QObject* child, d->controlFrame->paintopBox()->children()) {
child->removeEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::showStatusBar(bool toggled)
{
if (d->currentImageView && d->currentImageView->statusBar()) {
d->currentImageView->statusBar()->setVisible(toggled);
}
}
void KisViewManager::showJustTheCanvas(bool toggled)
{
KisConfig cfg;
KisMainWindow* main = mainWindow();
if(!main) {
dbgUI << "Unable to switch to canvas-only mode, main window not found";
return;
}
if (cfg.hideStatusbarFullscreen()) {
if(main->statusBar() && main->statusBar()->isVisible() == toggled) {
main->statusBar()->setVisible(!toggled);
}
}
if (cfg.hideDockersFullscreen()) {
KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
action->setCheckable(true);
if (action && action->isChecked() == toggled) {
action->setChecked(!toggled);
}
}
if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) {
if(toggled) {
main->setWindowState( main->windowState() | Qt::WindowFullScreen);
} else {
main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
}
}
if (cfg.hideMenuFullscreen()) {
if (main->menuBar()->isVisible() == toggled) {
main->menuBar()->setVisible(!toggled);
}
}
if (cfg.hideToolbarFullscreen()) {
QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
foreach(QToolBar* toolbar, toolBars) {
if (!toggled) {
if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
if (toolbar->property("wasvisible").toBool()) {
toolbar->setVisible(true);
}
}
}
else {
toolbar->setProperty("wasvisible", toolbar->isVisible());
toolbar->setVisible(false);
}
}
}
showHideScrollbars();
if (toggled) {
// show a fading heads-up display about the shortcut to go back
showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
actionCollection()->action("view_show_just_the_canvas")->shortcut().toString()), QIcon());
}
}
void KisViewManager::toggleTabletLogger()
{
d->inputManager->toggleTabletLogger();
}
void KisViewManager::openResourcesDirectory()
{
QString dir = KStandardDirs::locateLocal("data", "krita");
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
}
void KisViewManager::updateIcons()
{
if (mainWindow()) {
QList<QDockWidget*> dockers = mainWindow()->dockWidgets();
foreach(QDockWidget* dock, dockers) {
- kDebug() << "name " << dock->objectName();
+ dbgKrita << "name " << dock->objectName();
KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
if (titlebar) {
titlebar->updateIcons();
}
QObjectList objects;
objects.append(dock);
while (!objects.isEmpty()) {
QObject* object = objects.takeFirst();
objects.append(object->children());
KisIconUtils::updateIconCommon(object);
}
}
}
}
void KisViewManager::makeStatusBarVisible()
{
d->mainWindow->statusBar()->setVisible(true);
}
void KisViewManager::guiUpdateTimeout()
{
d->nodeManager->updateGUI();
d->selectionManager->updateGUI();
d->filterManager->updateGUI();
if (zoomManager()) {
zoomManager()->updateGUI();
}
d->gridManager->updateGUI();
d->perspectiveGridManager->updateGUI();
d->actionManager->updateGUI();
}
void KisViewManager::showFloatingMessage(const QString message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
{
if (!d->currentImageView) return;
d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment);
emit floatingMessageRequested(message, icon.name());
}
KisMainWindow *KisViewManager::mainWindow() const
{
return qobject_cast<KisMainWindow*>(d->mainWindow);
}
void KisViewManager::showHideScrollbars()
{
if (!d->currentImageView) return;
if (!d->currentImageView->canvasController()) return;
KisConfig cfg;
bool toggled = actionCollection()->action("view_show_just_the_canvas")->isChecked();
if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
} else {
d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}
}
void KisViewManager::setShowFloatingMessage(bool show)
{
d->showFloatingMessage = show;
}
void KisViewManager::changeAuthorProfile(const QString &profileName)
{
KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author");
if (profileName.isEmpty()) {
appAuthorGroup.writeEntry("active-profile", "");
} else if (profileName == i18nc("choice for author profile", "Anonymous")) {
appAuthorGroup.writeEntry("active-profile", "anonymous");
} else {
appAuthorGroup.writeEntry("active-profile", profileName);
}
appAuthorGroup.sync();
foreach(KisDocument *doc, KisPart::instance()->documents()) {
doc->documentInfo()->updateParameters();
}
}
void KisViewManager::slotUpdateAuthorProfileActions()
{
Q_ASSERT(d->actionAuthor);
if (!d->actionAuthor) {
return;
}
d->actionAuthor->clear();
d->actionAuthor->addAction(i18n("Default Author Profile"));
d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author");
QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
foreach (const QString &profile , profiles) {
d->actionAuthor->addAction(profile);
}
KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author");
QString profileName = appAuthorGroup.readEntry("active-profile", "");
if (profileName == "anonymous") {
d->actionAuthor->setCurrentItem(1);
} else if (profiles.contains(profileName)) {
d->actionAuthor->setCurrentAction(profileName);
} else {
d->actionAuthor->setCurrentItem(0);
}
}
diff --git a/krita/ui/PriorityQueue_p.h b/krita/ui/PriorityQueue_p.h
index 51b8eaffd6b..1ff4747435f 100644
--- a/krita/ui/PriorityQueue_p.h
+++ b/krita/ui/PriorityQueue_p.h
@@ -1,236 +1,236 @@
/* This file is part of the Calligra libraries
Copyright (C) 2001 Werner Trobin <trobin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef priority_queue_h
#define priority_queue_h
#include <vector>
#include <QString>
#include <QHash>
-#include <kdebug.h>
+#include <kis_debug.h>
// Better put all those internal classes in some namespace to avoid clashes
// as the names are quite generic
namespace CalligraFilter
{
/**
* This PriorityQueue class is implemented as "upside down" heap, i.e. the item
* with the \b smallest key is at the root of the heap.
* If you feel like using that class your template parameter has to have a public
* method "unsigned int key() const" which returns - surprise - the key. The
* supplied key must not be "negative." We use UINT_MAX as value to represent
* "infinity" for the shortest path algorithm.
* As this is a very specialized class we also demand a "void setIndex(int i)"
* method in order to tell nodes where they are located inside the heap. This is
* a very ugly approach, but it's the only way I see if you want to avoid a O(n)
* search for the item where you decreased the key :}
* Just to make it even worse we also use a "int index() const" method
* to fetch the index... well, you most likely would need one anyway ;)
* Note: This class is pointer based (like QPtr*) - if you create PriorityQueue<X>
* we actually operate on X* ! We don't care about deleting your pointers at all.
* We don't copy them, we don't create new ones,... - you own them, you have
* to delete them :)
*
* In case you change a key value make sure to call keyDecreased and pass the
* item you touched. This is running in O(log n) according to Cormen...
*
* All the ideas are stol^H^H^H^Hborrowed from "Introduction to Algorithms",
* Cormen et al
*
* @author Werner Trobin <trobin@kde.org>
*/
template<class T> class PriorityQueue
{
public:
PriorityQueue() {}
PriorityQueue(const PriorityQueue<T>& rhs) : m_vector(rhs.m_vector) {}
PriorityQueue(const QHash<QByteArray, T*>& items);
~PriorityQueue() {}
PriorityQueue<T> &operator=(const PriorityQueue<T>& rhs) {
m_vector = rhs.m_vector; return *this;
}
bool operator==(const PriorityQueue<T>& rhs) {
return m_vector == rhs.m_vector;
}
unsigned int count() const {
return m_vector.size();
}
bool isEmpty() const {
return m_vector.empty();
}
void insert(T* item);
/**
* Call this method after decreasing the key of the ith item. The heap
* properties will no longer be valid if you either forget to call that
* method, or if you \b increase the key.
*/
void keyDecreased(T* item);
T* extractMinimum();
/**
* For debugging
*/
void dump() const;
private:
// Note: We have to use a 1-based index here, and we get/return 0-based ones
int parent(int i) {
return ((i + 1) >> 1) - 1;
}
int left(int i) {
return ((i + 1) << 1) - 1;
}
int right(int i) {
return (i + 1) << 1;
}
void heapify(int i);
// item is !=0 for sure
void bubbleUp(T* item, int i);
// Builds the heap in the vector in O(n)
void buildHeap();
std::vector<T*> m_vector;
};
template<class T>
PriorityQueue<T>::PriorityQueue(const QHash<QByteArray, T*>& items)
: m_vector(items.count())
{
// First put all items into the vector
int i = 0;
foreach(T* item, items) {
item->setIndex(i);
m_vector[i] = item;
++i;
}
// Then build a heap in that vector
buildHeap();
}
template<class T>
void PriorityQueue<T>::insert(T* item)
{
if (!item)
return;
int i = static_cast<int>(m_vector.size());
m_vector.push_back(0); // extend the vector by one item. i == index to the last item
bubbleUp(item, i);
}
template<class T>
void PriorityQueue<T>::keyDecreased(T* item)
{
if (!item)
return;
bubbleUp(item, item->index());
}
template<class T>
T* PriorityQueue<T>::extractMinimum()
{
if (m_vector.size() < 1)
return 0;
T *min = m_vector[ 0 ];
m_vector[ 0 ] = m_vector[ m_vector.size() - 1 ];
// update the index
m_vector[ 0 ]->setIndex(0);
m_vector.pop_back();
heapify(0);
return min;
}
template<class T>
void PriorityQueue<T>::dump() const
{
- kDebug(30500) << "++++++++++ PriorityQueue::dump ++++++++++";
+ dbgFile << "++++++++++ PriorityQueue::dump ++++++++++";
QString out;
int size = static_cast<int>(m_vector.size());
for (int i = 0; i < size; ++i) {
if (m_vector[ i ]->index() != i)
out += " ERROR: index out of sync. Should be " + QString::number(i) + ", is " +
QString::number(m_vector[ i ]->index()) + ". ";
out += QString::number(m_vector[ i ]->key()) + ", ";
}
if (out.isEmpty())
out = "(empty)";
- kDebug(30500) << out;
- kDebug(30500) << "++++++++++ PriorityQueue::dump (done) ++++++++++";
+ dbgFile << out;
+ dbgFile << "++++++++++ PriorityQueue::dump (done) ++++++++++";
}
template<class T>
void PriorityQueue<T>::heapify(int i)
{
int l = left(i);
int r = right(i);
int size = static_cast<int>(m_vector.size());
int smallest;
if (l < size && i < size && m_vector[ l ]->key() < m_vector[ i ]->key())
smallest = l;
else
smallest = i;
if (r < size && m_vector[ r ]->key() < m_vector[ smallest ]->key())
smallest = r;
if (smallest != i) {
T* tmp = m_vector[ i ];
m_vector[ i ] = m_vector[ smallest ];
// update indices
m_vector[ i ]->setIndex(i);
tmp->setIndex(smallest);
m_vector[ smallest ] = tmp;
heapify(smallest);
}
}
template<class T>
void PriorityQueue<T>::bubbleUp(T* item, int i)
{
int p = parent(i);
while (i > 0 && m_vector[ p ]->key() > item->key()) {
// update the index first
m_vector[ p ]->setIndex(i);
// then move it there
m_vector[ i ] = m_vector[ p ];
i = p;
p = parent(i);
}
item->setIndex(i);
m_vector[ i ] = item;
}
template<class T>
void PriorityQueue<T>::buildHeap()
{
// from size() / 2 down to 1
for (int i = (m_vector.size() >> 1) - 1; i >= 0; --i)
heapify(i);
}
} // namespace Calligra
#endif // priority_queue_h
diff --git a/krita/ui/canvas/kis_canvas2.cpp b/krita/ui/canvas/kis_canvas2.cpp
index 5bd5fc625a8..e6e01bc5620 100644
--- a/krita/ui/canvas/kis_canvas2.cpp
+++ b/krita/ui/canvas/kis_canvas2.cpp
@@ -1,808 +1,808 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) Lukáš Tvrdý <lukast.dev@gmail.com>, (C) 2010
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA..
*/
#include "kis_canvas2.h"
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QTime>
#include <QLabel>
#include <QMouseEvent>
#include <QDesktopWidget>
#include <kis_debug.h>
#include <KoUnit.h>
#include <KoShapeManager.h>
#include <KoColorProfile.h>
#include <KoCanvasControllerWidget.h>
#include <KisDocument.h>
#include <KoSelection.h>
#include "kis_tool_proxy.h"
#include "kis_coordinates_converter.h"
#include "kis_prescaled_projection.h"
#include "kis_image.h"
#include "KisDocument.h"
#include "flake/kis_shape_layer.h"
#include "kis_canvas_resource_provider.h"
#include "KisViewManager.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_abstract_canvas_widget.h"
#include "kis_qpainter_canvas.h"
#include "kis_group_layer.h"
#include "flake/kis_shape_controller.h"
#include "kis_node_manager.h"
#include "kis_selection.h"
#include "kis_selection_component.h"
#include "flake/kis_shape_selection.h"
#include "kis_image_config.h"
#include "kis_infinity_manager.h"
#include "kis_signal_compressor.h"
#include "kis_display_color_converter.h"
#include "kis_exposure_gamma_correction_interface.h"
#include "KisView.h"
#include "kis_canvas_controller.h"
#ifdef HAVE_OPENGL
#include "opengl/kis_opengl_canvas2.h"
#endif
#include "opengl/kis_opengl.h"
#include <kis_favorite_resource_manager.h>
#include <kis_popup_palette.h>
#include "input/kis_input_manager.h"
#include "kis_painting_assistants_decoration.h"
#include "kis_canvas_updates_compressor.h"
class KisCanvas2::KisCanvas2Private
{
public:
KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer<KisView> view, KoCanvasResourceManager* resourceManager)
: coordinatesConverter(coordConverter)
, view(view)
, canvasWidget(0)
, shapeManager(new KoShapeManager(parent))
, currentCanvasIsOpenGL(false)
, toolProxy(new KisToolProxy(parent))
, vastScrolling(true)
, popupPalette(0)
, displayColorConverter(new KisDisplayColorConverter(resourceManager, view))
{
}
~KisCanvas2Private() {
delete shapeManager;
delete toolProxy;
}
KisCoordinatesConverter *coordinatesConverter;
QPointer<KisView>view;
KisAbstractCanvasWidget *canvasWidget;
KoShapeManager *shapeManager;
bool currentCanvasIsOpenGL;
#ifdef HAVE_OPENGL
int openGLFilterMode;
#endif
KisToolProxy *toolProxy;
KisPrescaledProjectionSP prescaledProjection;
bool vastScrolling;
KisSignalCompressor *updateSignalCompressor;
QRect savedUpdateRect;
QBitArray channelFlags;
KisPopupPalette *popupPalette;
KisDisplayColorConverter *displayColorConverter;
KisCanvasUpdatesCompressor projectionUpdatesCompressor;
};
KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceManager *resourceManager, QPointer<KisView>view, KoShapeBasedDocumentBase *sc)
: KoCanvasBase(sc, resourceManager)
, m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager))
{
// a bit of duplication from slotConfigChanged()
KisConfig cfg;
m_d->vastScrolling = cfg.vastScrolling();
createCanvas(cfg.useOpenGL());
connect(view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved(QPoint)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
/**
* We switch the shape manager every time vector layer or
* shape selection is activated. Flake does not expect this
* and connects all the signals of the global shape manager
* to the clients in the constructor. To workaround this we
* forward the signals of local shape managers stored in the
* vector layers to the signals of global shape manager. So the
* sequence of signal deliveries is the following:
*
* shapeLayer.m_d.canvas.m_shapeManager.selection() ->
* shapeLayer ->
* shapeController ->
* globalShapeManager.selection()
*/
KisShapeController *kritaShapeController = dynamic_cast<KisShapeController*>(sc);
connect(kritaShapeController, SIGNAL(selectionChanged()),
this, SLOT(slotSelectionChanged()));
connect(kritaShapeController, SIGNAL(selectionContentChanged()),
globalShapeManager(), SIGNAL(selectionContentChanged()));
connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)),
globalShapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*)));
m_d->updateSignalCompressor = new KisSignalCompressor(10 /*ms*/, KisSignalCompressor::FIRST_ACTIVE, this);
connect(m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
}
KisCanvas2::~KisCanvas2()
{
delete m_d;
}
void KisCanvas2::setCanvasWidget(QWidget * widget)
{
KisAbstractCanvasWidget *tmp = dynamic_cast<KisAbstractCanvasWidget*>(widget);
Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget");
if (m_d->popupPalette) {
m_d->popupPalette->setParent(widget);
}
if(m_d->canvasWidget != 0)
{
tmp->setDecorations(m_d->canvasWidget->decorations());
// Redundant check for the constructor case, see below
if(viewManager() != 0)
viewManager()->inputManager()->removeTrackedCanvas(this);
}
m_d->canvasWidget = tmp;
// Either tmp was null or we are being called by KisCanvas2 constructor that is called by KisView
// constructor, so the view manager still doesn't exists.
if(m_d->canvasWidget != 0 && viewManager() != 0)
viewManager()->inputManager()->addTrackedCanvas(this);
if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) {
KisInfinityManager *manager = new KisInfinityManager(m_d->view, this);
manager->setVisible(true);
m_d->canvasWidget->addDecoration(manager);
}
widget->setAutoFillBackground(false);
widget->setAttribute(Qt::WA_OpaquePaintEvent);
widget->setMouseTracking(true);
widget->setAcceptDrops(true);
KoCanvasControllerWidget *controller = dynamic_cast<KoCanvasControllerWidget*>(canvasController());
if (controller) {
Q_ASSERT(controller->canvas() == this);
controller->changeCanvasWidget(widget);
}
}
bool KisCanvas2::canvasIsOpenGL()
{
return m_d->currentCanvasIsOpenGL;
}
void KisCanvas2::gridSize(qreal *horizontal, qreal *vertical) const
{
Q_ASSERT(horizontal);
Q_ASSERT(vertical);
QTransform transform = coordinatesConverter()->imageToDocumentTransform();
QPointF size = transform.map(QPointF(m_d->view->document()->gridData().gridX(), m_d->view->document()->gridData().gridY()));
*horizontal = size.x();
*vertical = size.y();
}
bool KisCanvas2::snapToGrid() const
{
return m_d->view->document()->gridData().snapToGrid();
}
qreal KisCanvas2::rotationAngle() const
{
return m_d->coordinatesConverter->rotationAngle();
}
bool KisCanvas2::xAxisMirrored() const
{
return m_d->coordinatesConverter->xAxisMirrored();
}
bool KisCanvas2::yAxisMirrored() const
{
return m_d->coordinatesConverter->yAxisMirrored();
}
void KisCanvas2::channelSelectionChanged()
{
KisImageWSP image = this->image();
m_d->channelFlags = image->rootLayer()->channelFlags();
image->barrierLock();
m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
startUpdateInPatches(image->bounds());
image->unlock();
}
void KisCanvas2::addCommand(KUndo2Command *command)
{
// This method exists to support flake-related operations
m_d->view->document()->addCommand(command);
}
KoShapeManager* KisCanvas2::shapeManager() const
{
if (!viewManager()) return m_d->shapeManager;
if (!viewManager()->nodeManager()) return m_d->shapeManager;
KisLayerSP activeLayer = viewManager()->nodeManager()->activeLayer();
if (activeLayer && activeLayer->isEditable()) {
KisShapeLayer * shapeLayer = dynamic_cast<KisShapeLayer*>(activeLayer.data());
if (shapeLayer) {
return shapeLayer->shapeManager();
}
KisSelectionSP selection = activeLayer->selection();
if (selection && !selection.isNull()) {
if (selection->hasShapeSelection()) {
KoShapeManager* m = dynamic_cast<KisShapeSelection*>(selection->shapeSelection())->shapeManager();
return m;
}
}
}
return m_d->shapeManager;
}
KoShapeManager * KisCanvas2::globalShapeManager() const
{
return m_d->shapeManager;
}
void KisCanvas2::updateInputMethodInfo()
{
// TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget...
}
const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const
{
return m_d->coordinatesConverter;
}
KoViewConverter* KisCanvas2::viewConverter() const
{
return m_d->coordinatesConverter;
}
KisInputManager* KisCanvas2::globalInputManager() const
{
return m_d->view->globalInputManager();
}
QWidget* KisCanvas2::canvasWidget()
{
return m_d->canvasWidget->widget();
}
const QWidget* KisCanvas2::canvasWidget() const
{
return m_d->canvasWidget->widget();
}
KoUnit KisCanvas2::unit() const
{
KoUnit unit(KoUnit::Pixel);
KisImageWSP image = m_d->view->image();
if (image) {
if (!qFuzzyCompare(image->xRes(), image->yRes())) {
- qWarning() << "WARNING: resolution of the image is anisotropic"
+ warnKrita << "WARNING: resolution of the image is anisotropic"
<< ppVar(image->xRes())
<< ppVar(image->yRes());
}
const qreal resolution = image->xRes();
unit.setFactor(resolution);
}
return unit;
}
KoToolProxy * KisCanvas2::toolProxy() const
{
return m_d->toolProxy;
}
void KisCanvas2::createQPainterCanvas()
{
m_d->currentCanvasIsOpenGL = false;
KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view);
m_d->prescaledProjection = new KisPrescaledProjection();
m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter);
m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter->monitorProfile(),
m_d->displayColorConverter->renderingIntent(),
m_d->displayColorConverter->conversionFlags());
m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter->displayFilter());
canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
setCanvasWidget(canvasWidget);
}
void KisCanvas2::createOpenGLCanvas()
{
#ifdef HAVE_OPENGL
KisConfig cfg;
m_d->openGLFilterMode = cfg.openGLFilteringMode();
m_d->currentCanvasIsOpenGL = true;
KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->view->image(), m_d->displayColorConverter);
setCanvasWidget(canvasWidget);
#else
qFatal("Bad use of createOpenGLCanvas(). It shouldn't have happened =(");
#endif
}
void KisCanvas2::createCanvas(bool useOpenGL)
{
KisConfig cfg;
QDesktopWidget dw;
const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView()));
m_d->displayColorConverter->setMonitorProfile(profile);
if (useOpenGL) {
#ifdef HAVE_OPENGL
if (KisOpenGL::hasOpenGL()) {
createOpenGLCanvas();
if (cfg.canvasState() == "OPENGL_FAILED") {
// Creating the opengl canvas failed, fall back
warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter.";
createQPainterCanvas();
}
} else {
warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
createQPainterCanvas();
}
#else
warnKrita << "User requested an OpenGL canvas, but Krita was compiled without OpenGL support. Falling back to QPainter.";
createQPainterCanvas();
#endif
} else {
createQPainterCanvas();
}
if (m_d->popupPalette) {
m_d->popupPalette->setParent(m_d->canvasWidget->widget());
}
}
void KisCanvas2::initializeImage()
{
KisImageWSP image = m_d->view->image();
m_d->coordinatesConverter->setImage(image);
connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
connect(this, SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection()));
connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection);
connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32)));
connectCurrentCanvas();
}
void KisCanvas2::connectCurrentCanvas()
{
KisImageWSP image = m_d->view->image();
if (!m_d->currentCanvasIsOpenGL) {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setImage(image);
}
startResizingImage();
emit imageChanged(image);
}
void KisCanvas2::disconnectCurrentCanvas()
{
m_d->canvasWidget->disconnectCurrentCanvas();
}
void KisCanvas2::resetCanvas(bool useOpenGL)
{
// we cannot reset the canvas before it's created, but this method might be called,
// for instance when setting the monitor profile.
if (!m_d->canvasWidget) {
return;
}
#ifdef HAVE_OPENGL
KisConfig cfg;
bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) ||
(m_d->currentCanvasIsOpenGL &&
m_d->openGLFilterMode != cfg.openGLFilteringMode());
if (needReset) {
disconnectCurrentCanvas();
createCanvas(useOpenGL);
connectCurrentCanvas();
notifyZoomChanged();
}
#else
Q_UNUSED(useOpenGL)
#endif
updateCanvasWidgetImpl();
}
void KisCanvas2::startUpdateInPatches(const QRect &imageRect)
{
if (m_d->currentCanvasIsOpenGL) {
startUpdateCanvasProjection(imageRect);
} else {
KisImageConfig imageConfig;
int patchWidth = imageConfig.updatePatchWidth();
int patchHeight = imageConfig.updatePatchHeight();
for (int y = 0; y < imageRect.height(); y += patchHeight) {
for (int x = 0; x < imageRect.width(); x += patchWidth) {
QRect patchRect(x, y, patchWidth, patchHeight);
startUpdateCanvasProjection(patchRect);
}
}
}
}
void KisCanvas2::setDisplayFilter(KisDisplayFilter *displayFilter)
{
m_d->displayColorConverter->setDisplayFilter(displayFilter);
KisImageWSP image = this->image();
image->barrierLock();
m_d->canvasWidget->setDisplayFilter(displayFilter);
image->unlock();
}
KisDisplayFilter *KisCanvas2::displayFilter() const
{
return m_d->displayColorConverter->displayFilter();
}
KisDisplayColorConverter* KisCanvas2::displayColorConverter() const
{
return m_d->displayColorConverter;
}
KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const
{
KisDisplayFilter *displayFilter =
m_d->displayColorConverter->displayFilter();
return displayFilter ?
displayFilter->correctionInterface() :
KisDumbExposureGammaCorrectionInterface::instance();
}
void KisCanvas2::startResizingImage()
{
KisImageWSP image = this->image();
qint32 w = image->width();
qint32 h = image->height();
emit sigContinueResizeImage(w, h);
QRect imageBounds(0, 0, w, h);
startUpdateInPatches(imageBounds);
}
void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
{
m_d->canvasWidget->finishResizingImage(w, h);
}
void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
{
KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc, m_d->channelFlags);
if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
emit sigCanvasCacheUpdated();
}
}
void KisCanvas2::updateCanvasProjection()
{
while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
QRect vRect = m_d->canvasWidget->updateCanvasProjection(info);
if (!vRect.isEmpty()) {
updateCanvasWidgetImpl(m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect());
}
}
// TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
if (m_d->currentCanvasIsOpenGL) {
updateCanvasWidgetImpl();
}
}
void KisCanvas2::slotDoCanvasUpdate()
{
if (m_d->canvasWidget->isBusy()) {
// just restarting the timer
updateCanvasWidgetImpl(m_d->savedUpdateRect);
return;
}
if (m_d->savedUpdateRect.isEmpty()) {
m_d->canvasWidget->widget()->update();
emit updateCanvasRequested(m_d->canvasWidget->widget()->rect());
} else {
emit updateCanvasRequested(m_d->savedUpdateRect);
m_d->canvasWidget->widget()->update(m_d->savedUpdateRect);
}
m_d->savedUpdateRect = QRect();
}
void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc)
{
if (!m_d->updateSignalCompressor->isActive() ||
!m_d->savedUpdateRect.isEmpty()) {
m_d->savedUpdateRect |= rc;
}
m_d->updateSignalCompressor->start();
}
void KisCanvas2::updateCanvas()
{
updateCanvasWidgetImpl();
}
void KisCanvas2::updateCanvas(const QRectF& documentRect)
{
if (m_d->currentCanvasIsOpenGL && m_d->canvasWidget->decorations().size() > 0) {
updateCanvasWidgetImpl();
}
else {
// updateCanvas is called from tools, never from the projection
// updates, so no need to prescale!
QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect();
widgetRect.adjust(-2, -2, 2, 2);
if (!widgetRect.isEmpty()) {
updateCanvasWidgetImpl(widgetRect);
}
}
}
void KisCanvas2::disconnectCanvasObserver(QObject *object)
{
KoCanvasBase::disconnectCanvasObserver(object);
m_d->view->disconnect(object);
}
void KisCanvas2::notifyZoomChanged()
{
if (!m_d->currentCanvasIsOpenGL) {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->notifyZoomChanged();
}
updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction
}
void KisCanvas2::preScale()
{
if (!m_d->currentCanvasIsOpenGL) {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->preScale();
}
}
const KoColorProfile * KisCanvas2::monitorProfile()
{
return m_d->displayColorConverter->monitorProfile();
}
KisViewManager* KisCanvas2::viewManager() const
{
if (m_d->view) {
return m_d->view->viewManager();
}
return 0;
}
QPointer<KisView>KisCanvas2::imageView() const
{
return m_d->view;
}
KisImageWSP KisCanvas2::image() const
{
return m_d->view->image();
}
KisImageWSP KisCanvas2::currentImage() const
{
return m_d->view->image();
}
void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
{
QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
m_d->coordinatesConverter->setDocumentOffset(documentOffset);
QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
QPointF moveOffset = offsetAfter - offsetBefore;
if (!m_d->currentCanvasIsOpenGL)
m_d->prescaledProjection->viewportMoved(moveOffset);
emit documentOffsetUpdateFinished();
updateCanvas();
}
void KisCanvas2::slotConfigChanged()
{
KisConfig cfg;
m_d->vastScrolling = cfg.vastScrolling();
resetCanvas(cfg.useOpenGL());
slotSetDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget())));
}
void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile)
{
if (m_d->displayColorConverter->monitorProfile() == monitorProfile) return;
m_d->displayColorConverter->setMonitorProfile(monitorProfile);
KisImageWSP image = this->image();
image->barrierLock();
m_d->canvasWidget->setDisplayProfile(m_d->displayColorConverter);
startUpdateInPatches(image->bounds());
image->unlock();
}
void KisCanvas2::addDecoration(KisCanvasDecoration* deco)
{
m_d->canvasWidget->addDecoration(deco);
}
KisCanvasDecoration* KisCanvas2::decoration(const QString& id) const
{
return m_d->canvasWidget->decoration(id);
}
QPoint KisCanvas2::documentOrigin() const
{
/**
* In Krita we don't use document origin anymore.
* All the centering when needed (vastScrolling < 0.5) is done
* automatically by the KisCoordinatesConverter.
*/
return QPoint();
}
QPoint KisCanvas2::documentOffset() const
{
return m_d->coordinatesConverter->documentOffset();
}
void KisCanvas2::setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager)
{
m_d->popupPalette = new KisPopupPalette(favoriteResourceManager, displayColorConverter()->displayRendererInterface(), m_d->canvasWidget->widget());
m_d->popupPalette->showPopupPalette(false);
}
void KisCanvas2::setCursor(const QCursor &cursor)
{
canvasWidget()->setCursor(cursor);
}
void KisCanvas2::slotSelectionChanged()
{
KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(viewManager()->activeLayer().data());
if (!shapeLayer) {
return;
}
m_d->shapeManager->selection()->deselectAll();
foreach(KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) {
m_d->shapeManager->selection()->select(shape);
}
}
bool KisCanvas2::isPopupPaletteVisible()
{
if (!m_d->popupPalette) {
return false;
}
return m_d->popupPalette->isVisible();
}
void KisCanvas2::setWrapAroundViewingMode(bool value)
{
KisCanvasDecoration *infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
infinityDecoration->setVisible(!value);
}
m_d->canvasWidget->setWrapAroundViewingMode(value);
}
bool KisCanvas2::wrapAroundViewingMode() const
{
KisCanvasDecoration *infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
return !(infinityDecoration->visible());
}
return false;
}
KoGuidesData *KisCanvas2::guidesData()
{
return &m_d->view->document()->guidesData();
}
void KisCanvas2::slotShowPopupPalette(const QPoint &p)
{
if (!m_d->popupPalette) {
return;
}
m_d->popupPalette->showPopupPalette(p);
}
KisPaintingAssistantsDecoration* KisCanvas2::paintingAssistantsDecoration()
{
KisCanvasDecoration* deco = decoration("paintingAssistantsDecoration");
return qobject_cast<KisPaintingAssistantsDecoration*>(deco);
}
diff --git a/krita/ui/canvas/kis_canvas_controller.cpp b/krita/ui/canvas/kis_canvas_controller.cpp
index 43c00ff1d88..9bf9f4833c7 100644
--- a/krita/ui/canvas/kis_canvas_controller.cpp
+++ b/krita/ui/canvas/kis_canvas_controller.cpp
@@ -1,247 +1,245 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_canvas_controller.h"
#include <QMouseEvent>
#include <QTabletEvent>
#include <klocale.h>
#include "kis_canvas_decoration.h"
#include "kis_paintop_transformation_connector.h"
#include "kis_coordinates_converter.h"
#include "kis_canvas2.h"
#include "kis_image.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "input/kis_tablet_event.h"
#include "krita_utils.h"
struct KisCanvasController::Private {
Private(KisCanvasController *qq)
: q(qq),
paintOpTransformationConnector(0)
{
}
QPointer<KisView>view;
KisCoordinatesConverter *coordinatesConverter;
KisCanvasController *q;
KisPaintopTransformationConnector *paintOpTransformationConnector;
void emitPointerPositionChangedSignals(QEvent *event);
void updateDocumentSizeAfterTransform();
void showRotationValueOnCanvas();
void showMirrorStateOnCanvas();
};
void KisCanvasController::Private::emitPointerPositionChangedSignals(QEvent *event)
{
if (!coordinatesConverter) return;
QPoint pointerPos;
if (QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event)) {
pointerPos = mouseEvent->pos();
} else if (QTabletEvent *tabletEvent = dynamic_cast<QTabletEvent*>(event)) {
pointerPos = tabletEvent->pos();
- } else if (KisTabletEvent *kisTabletEvent = dynamic_cast<KisTabletEvent*>(event)) {
- pointerPos = kisTabletEvent->pos();
}
QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos);
q->proxyObject->emitDocumentMousePositionChanged(documentPos);
q->proxyObject->emitCanvasMousePositionChanged(pointerPos);
}
void KisCanvasController::Private::updateDocumentSizeAfterTransform()
{
// round the size of the area to the nearest integer instead of getting aligned rect
QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size();
q->updateDocumentSize(widgetSize, true);
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(q->canvas());
Q_ASSERT(kritaCanvas);
kritaCanvas->notifyZoomChanged();
}
KisCanvasController::KisCanvasController(QPointer<KisView>parent, KActionCollection * actionCollection)
: KoCanvasControllerWidget(actionCollection, parent),
m_d(new Private(this))
{
m_d->view = parent;
}
KisCanvasController::~KisCanvasController()
{
delete m_d;
}
void KisCanvasController::setCanvas(KoCanvasBase *canvas)
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas);
Q_ASSERT(kritaCanvas);
m_d->coordinatesConverter =
const_cast<KisCoordinatesConverter*>(kritaCanvas->coordinatesConverter());
KoCanvasControllerWidget::setCanvas(canvas);
m_d->paintOpTransformationConnector =
new KisPaintopTransformationConnector(kritaCanvas, this);
}
void KisCanvasController::changeCanvasWidget(QWidget *widget)
{
KoCanvasControllerWidget::changeCanvasWidget(widget);
}
void KisCanvasController::activate()
{
KoCanvasControllerWidget::activate();
}
void KisCanvasController::keyPressEvent(QKeyEvent *event)
{
/**
* Dirty Hack Alert:
* Do not call the KoCanvasControllerWidget::keyPressEvent()
* to avoid activation of Pan and Default tool activation shortcuts
*/
Q_UNUSED(event);
}
void KisCanvasController::wheelEvent(QWheelEvent *event)
{
/**
* Dirty Hack Alert:
* Do not call the KoCanvasControllerWidget::wheelEvent()
* to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea
*/
Q_UNUSED(event);
}
bool KisCanvasController::eventFilter(QObject *watched, QEvent *event)
{
KoCanvasBase *canvas = this->canvas();
if (canvas && canvas->canvasWidget() && (watched == canvas->canvasWidget())) {
- if (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove || event->type() == (QEvent::Type)KisTabletEvent::TabletMoveEx) {
+ if (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) {
m_d->emitPointerPositionChangedSignals(event);
- return false;
+ return true;
}
}
return KoCanvasControllerWidget::eventFilter(watched, event);
}
void KisCanvasController::updateDocumentSize(const QSize &sz, bool recalculateCenter)
{
KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter);
emit documentSizeChanged();
}
void KisCanvasController::Private::showMirrorStateOnCanvas()
{
bool isXMirrored = coordinatesConverter->xAxisMirrored();
view->viewManager()->
showFloatingMessage(
i18nc("floating message about mirroring",
"Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")),
QIcon(), 500, KisFloatingMessage::Low);
}
void KisCanvasController::mirrorCanvas(bool enable)
{
KisCanvasDecoration *decorator = dynamic_cast<KisCanvas2*>(this->canvas())->decoration("mirror_axis");
KIS_ASSERT_RECOVER_RETURN(decorator);
decorator->setVisible(enable);
QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false);
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showMirrorStateOnCanvas();
}
void KisCanvasController::Private::showRotationValueOnCanvas()
{
qreal rotationAngle = coordinatesConverter->rotationAngle();
view->viewManager()->
showFloatingMessage(
i18nc("floating message about rotation", "Rotation: %1° ",
KritaUtils::prettyFormatReal(rotationAngle)),
QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter);
}
void KisCanvasController::rotateCanvas(qreal angle)
{
QPoint newOffset = m_d->coordinatesConverter->rotate(m_d->coordinatesConverter->widgetCenterPoint(), angle);
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showRotationValueOnCanvas();
}
void KisCanvasController::rotateCanvasRight15()
{
rotateCanvas(15.0);
}
void KisCanvasController::rotateCanvasLeft15()
{
rotateCanvas(-15.0);
}
void KisCanvasController::resetCanvasRotation()
{
QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint());
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showRotationValueOnCanvas();
}
void KisCanvasController::slotToggleWrapAroundMode(bool value)
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
if (!canvas()->canvasIsOpenGL() && value) {
m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n"
"To visualize wrap-around mode, enable OpenGL."), QIcon());
}
kritaCanvas->setWrapAroundViewingMode(value);
kritaCanvas->image()->setWrapAroundModePermitted(value);
}
bool KisCanvasController::wrapAroundMode() const
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->wrapAroundViewingMode();
}
diff --git a/krita/ui/canvas/kis_display_color_converter.cpp b/krita/ui/canvas/kis_display_color_converter.cpp
index 3c82e85bfee..f190d91f780 100644
--- a/krita/ui/canvas/kis_display_color_converter.cpp
+++ b/krita/ui/canvas/kis_display_color_converter.cpp
@@ -1,627 +1,627 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_display_color_converter.h"
#include <kglobal.h>
#include <KoColor.h>
#include <KoColorDisplayRendererInterface.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorConversions.h>
#include <KoCanvasResourceManager.h>
#include "kis_config_notifier.h"
#include "kis_canvas_resource_provider.h"
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kundo2command.h"
#include "kis_config.h"
#include "kis_paint_device.h"
#include "kis_iterator_ng.h"
struct KisDisplayColorConverter::Private
{
Private(KisDisplayColorConverter *_q, KoCanvasResourceManager *_resourceManager)
: q(_q),
resourceManager(_resourceManager),
nodeColorSpace(0),
paintingColorSpace(0),
monitorColorSpace(0),
monitorProfile(0),
renderingIntent(KoColorConversionTransformation::InternalRenderingIntent),
conversionFlags(KoColorConversionTransformation::InternalConversionFlags),
displayFilter(0),
intermediateColorSpace(0),
displayRenderer(new DisplayRenderer(_q, _resourceManager))
{
}
KisDisplayColorConverter * const q;
KoCanvasResourceManager *resourceManager;
const KoColorSpace *nodeColorSpace;
const KoColorSpace *paintingColorSpace;
const KoColorSpace *monitorColorSpace;
const KoColorProfile *monitorProfile;
KoColorConversionTransformation::Intent renderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags;
KisDisplayFilter *displayFilter;
const KoColorSpace *intermediateColorSpace;
KoColor intermediateFgColor;
KisNodeSP connectedNode;
inline KoColor approximateFromQColor(const QColor &qcolor);
inline QColor approximateToQColor(const KoColor &color);
void slotCanvasResourceChanged(int key, const QVariant &v);
void slotUpdateCurrentNodeColorSpace();
void selectPaintingColorSpace();
void updateIntermediateFgColor(const KoColor &color);
void setCurrentNode(KisNodeSP node);
bool useOcio() const;
bool finalIsRgba(const KoColorSpace *cs) const;
template <bool flipToBgra>
QColor floatArrayToQColor(const float *p);
template <bool flipToBgra>
QImage convertToQImageDirect(KisPaintDeviceSP device);
class DisplayRenderer : public KoColorDisplayRendererInterface {
public:
DisplayRenderer(KisDisplayColorConverter *parent, KoCanvasResourceManager *resourceManager)
: m_parent(parent),
m_resourceManager(resourceManager)
{
parent->connect(parent, SIGNAL(displayConfigurationChanged()),
this, SIGNAL(displayConfigurationChanged()));
}
QColor toQColor(const KoColor &c) const {
return m_parent->toQColor(c);
}
KoColor approximateFromRenderedQColor(const QColor &c) const {
return m_parent->approximateFromRenderedQColor(c);
}
KoColor fromHsv(int h, int s, int v, int a) const {
return m_parent->fromHsv(h, s, v, a);
}
void getHsv(const KoColor &srcColor, int *h, int *s, int *v, int *a) const {
m_parent->getHsv(srcColor, h, s, v, a);
}
virtual qreal minVisibleFloatValue(const KoChannelInfo *chaninfo) const {
return chaninfo->getUIMin();
}
virtual qreal maxVisibleFloatValue(const KoChannelInfo *chaninfo) const {
qreal maxValue = chaninfo->getUIMax();
if (m_resourceManager) {
qreal exposure = m_resourceManager->resource(KisCanvasResourceProvider::HdrExposure).value<qreal>();
// not sure if *= is what we want
maxValue *= std::pow(2.0, -exposure);
}
return maxValue;
}
private:
KisDisplayColorConverter *m_parent;
KoCanvasResourceManager *m_resourceManager;
};
QScopedPointer<KoColorDisplayRendererInterface> displayRenderer;
};
KisDisplayColorConverter::KisDisplayColorConverter(KoCanvasResourceManager *resourceManager, QObject *parent)
: QObject(parent),
m_d(new Private(this, resourceManager))
{
connect(m_d->resourceManager, SIGNAL(canvasResourceChanged(int, const QVariant&)),
SLOT(slotCanvasResourceChanged(int, const QVariant&)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(selectPaintingColorSpace()));
m_d->setCurrentNode(0);
setMonitorProfile(0);
setDisplayFilter(0);
}
KisDisplayColorConverter::KisDisplayColorConverter()
: m_d(new Private(this, 0))
{
setDisplayFilter(0);
delete m_d->displayFilter;
m_d->paintingColorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_d->setCurrentNode(0);
setMonitorProfile(0);
}
KisDisplayColorConverter::~KisDisplayColorConverter()
{
}
KisDisplayColorConverter* KisDisplayColorConverter::dumbConverterInstance()
{
K_GLOBAL_STATIC(KisDisplayColorConverter, s_instance);
return s_instance;
}
KoColorDisplayRendererInterface* KisDisplayColorConverter::displayRendererInterface() const
{
return m_d->displayRenderer.data();
}
bool KisDisplayColorConverter::Private::useOcio() const
{
return displayFilter && paintingColorSpace->colorModelId() == RGBAColorModelID;
}
void KisDisplayColorConverter::Private::updateIntermediateFgColor(const KoColor &srcColor)
{
KIS_ASSERT_RECOVER_RETURN(displayFilter);
KoColor color = srcColor;
color.convertTo(intermediateColorSpace);
displayFilter->approximateForwardTransformation(color.data(), 1);
intermediateFgColor = color;
}
void KisDisplayColorConverter::Private::slotCanvasResourceChanged(int key, const QVariant &v)
{
if (key == KisCanvasResourceProvider::CurrentKritaNode) {
KisNodeSP currentNode = v.value<KisNodeWSP>();
setCurrentNode(currentNode);
} else if (useOcio() && key == KoCanvasResourceManager::ForegroundColor) {
updateIntermediateFgColor(v.value<KoColor>());
}
}
void KisDisplayColorConverter::Private::slotUpdateCurrentNodeColorSpace()
{
setCurrentNode(connectedNode);
}
inline KisPaintDeviceSP findValidDevice(KisNodeSP node) {
return node->paintDevice() ? node->paintDevice() : node->original();
}
void KisDisplayColorConverter::Private::setCurrentNode(KisNodeSP node)
{
if (connectedNode) {
KisPaintDeviceSP device = findValidDevice(connectedNode);
if (device) {
q->disconnect(device, 0);
}
}
if (node) {
KisPaintDeviceSP device = findValidDevice(node);
nodeColorSpace = device ?
device->compositionSourceColorSpace() :
node->colorSpace();
KIS_ASSERT_RECOVER_NOOP(nodeColorSpace);
if (device) {
q->connect(device, SIGNAL(profileChanged(const KoColorProfile*)),
SLOT(slotUpdateCurrentNodeColorSpace()), Qt::UniqueConnection);
q->connect(device, SIGNAL(colorSpaceChanged(const KoColorSpace*)),
SLOT(slotUpdateCurrentNodeColorSpace()), Qt::UniqueConnection);
}
} else {
nodeColorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
connectedNode = node;
selectPaintingColorSpace();
}
void KisDisplayColorConverter::Private::selectPaintingColorSpace()
{
KisConfig cfg;
paintingColorSpace = cfg.customColorSelectorColorSpace();
if (!paintingColorSpace || displayFilter) {
paintingColorSpace = nodeColorSpace;
}
emit q->displayConfigurationChanged();
}
const KoColorSpace* KisDisplayColorConverter::paintingColorSpace() const
{
KIS_ASSERT_RECOVER(m_d->paintingColorSpace) {
return KoColorSpaceRegistry::instance()->rgb8();
}
return m_d->paintingColorSpace;
}
void KisDisplayColorConverter::setMonitorProfile(const KoColorProfile *monitorProfile)
{
m_d->monitorColorSpace = KoColorSpaceRegistry::instance()->rgb8(monitorProfile);
m_d->monitorProfile = monitorProfile;
m_d->renderingIntent = renderingIntent();
m_d->conversionFlags = conversionFlags();
emit displayConfigurationChanged();
}
void KisDisplayColorConverter::setDisplayFilter(KisDisplayFilter *displayFilter)
{
if (m_d->displayFilter && displayFilter &&
displayFilter->lockCurrentColorVisualRepresentation()) {
KoColor color(m_d->intermediateFgColor);
displayFilter->approximateInverseTransformation(color.data(), 1);
color.convertTo(m_d->paintingColorSpace);
m_d->resourceManager->setForegroundColor(color);
}
m_d->displayFilter = displayFilter;
m_d->intermediateColorSpace = 0;
if (m_d->displayFilter) {
// choosing default profile, which is scRGB
const KoColorProfile *intermediateProfile = 0;
m_d->intermediateColorSpace =
KoColorSpaceRegistry::instance()->
colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), intermediateProfile);
KIS_ASSERT_RECOVER(m_d->intermediateColorSpace) {
m_d->intermediateColorSpace = m_d->monitorColorSpace;
}
m_d->updateIntermediateFgColor(
m_d->resourceManager->foregroundColor());
}
{ // sanity check
KisConfig cfg;
//KIS_ASSERT_RECOVER_NOOP(cfg.useOcio() == (bool) m_d->displayFilter);
}
m_d->selectPaintingColorSpace();
}
KoColorConversionTransformation::Intent
KisDisplayColorConverter::renderingIntent()
{
KisConfig cfg;
return (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent();
}
KoColorConversionTransformation::ConversionFlags
KisDisplayColorConverter::conversionFlags()
{
KoColorConversionTransformation::ConversionFlags conversionFlags =
KoColorConversionTransformation::HighQuality;
KisConfig cfg;
if (cfg.useBlackPointCompensation()) conversionFlags |= KoColorConversionTransformation::BlackpointCompensation;
if (!cfg.allowLCMSOptimization()) conversionFlags |= KoColorConversionTransformation::NoOptimization;
return conversionFlags;
}
KisDisplayFilter *KisDisplayColorConverter::displayFilter() const
{
return m_d->displayFilter;
}
const KoColorProfile* KisDisplayColorConverter::monitorProfile() const
{
return m_d->monitorProfile;
}
bool KisDisplayColorConverter::Private::finalIsRgba(const KoColorSpace *cs) const
{
/**
* In Krita RGB color spaces differ: 8/16bit are BGRA, 16f/32f-bit RGBA
*/
KoID colorDepthId = cs->colorDepthId();
return colorDepthId == Float16BitsColorDepthID ||
colorDepthId == Float32BitsColorDepthID;
}
template <bool flipToBgra>
QColor KisDisplayColorConverter::Private::floatArrayToQColor(const float *p) {
if (flipToBgra) {
return QColor(KoColorSpaceMaths<float, quint8>::scaleToA(p[0]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[1]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[2]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[3]));
} else {
return QColor(KoColorSpaceMaths<float, quint8>::scaleToA(p[2]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[1]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[0]),
KoColorSpaceMaths<float, quint8>::scaleToA(p[3]));
}
}
QColor KisDisplayColorConverter::toQColor(const KoColor &srcColor) const
{
KoColor c(srcColor);
c.convertTo(m_d->paintingColorSpace);
if (!m_d->useOcio()) {
// we expect the display profile is rgb8, which is BGRA here
KIS_ASSERT_RECOVER(m_d->monitorColorSpace->pixelSize() == 4) { return Qt::red; };
c.convertTo(m_d->monitorColorSpace, m_d->renderingIntent, m_d->conversionFlags);
const quint8 *p = c.data();
return QColor(p[2], p[1], p[0], p[3]);
} else {
const KoColorSpace *srcCS = c.colorSpace();
if (m_d->displayFilter->useInternalColorManagement()) {
srcCS = KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
m_d->monitorProfile);
c.convertTo(srcCS, m_d->renderingIntent, m_d->conversionFlags);
}
int numChannels = srcCS->channelCount();
QVector<float> normalizedChannels(numChannels);
srcCS->normalisedChannelsValue(c.data(), normalizedChannels);
m_d->displayFilter->filter((quint8*)normalizedChannels.data(), 1);
const float *p = (const float *)normalizedChannels.constData();
return m_d->finalIsRgba(srcCS) ?
m_d->floatArrayToQColor<true>(p) :
m_d->floatArrayToQColor<false>(p);
}
}
KoColor KisDisplayColorConverter::approximateFromRenderedQColor(const QColor &c) const
{
return m_d->approximateFromQColor(c);
}
template <bool flipToBgra>
QImage
KisDisplayColorConverter::Private::convertToQImageDirect(KisPaintDeviceSP device)
{
QRect bounds = device->exactBounds();
if (bounds.isEmpty()) return QImage();
QImage image(bounds.size(), QImage::Format_ARGB32);
KisSequentialConstIterator it(device, bounds);
quint8 *dstPtr = image.bits();
const KoColorSpace *cs = device->colorSpace();
int numChannels = cs->channelCount();
QVector<float> normalizedChannels(numChannels);
do {
cs->normalisedChannelsValue(it.rawDataConst(), normalizedChannels);
displayFilter->filter((quint8*)normalizedChannels.data(), 1);
const float *p = normalizedChannels.constData();
if (flipToBgra) {
dstPtr[0] = KoColorSpaceMaths<float, quint8>::scaleToA(p[2]);
dstPtr[1] = KoColorSpaceMaths<float, quint8>::scaleToA(p[1]);
dstPtr[2] = KoColorSpaceMaths<float, quint8>::scaleToA(p[0]);
dstPtr[3] = KoColorSpaceMaths<float, quint8>::scaleToA(p[3]);
} else {
dstPtr[0] = KoColorSpaceMaths<float, quint8>::scaleToA(p[0]);
dstPtr[1] = KoColorSpaceMaths<float, quint8>::scaleToA(p[1]);
dstPtr[2] = KoColorSpaceMaths<float, quint8>::scaleToA(p[2]);
dstPtr[3] = KoColorSpaceMaths<float, quint8>::scaleToA(p[3]);
}
dstPtr += 4;
} while (it.nextPixel());
return image;
}
QImage KisDisplayColorConverter::toQImage(KisPaintDeviceSP srcDevice) const
{
KisPaintDeviceSP device = srcDevice;
if (!(*device->colorSpace() == *m_d->paintingColorSpace)) {
device = new KisPaintDevice(*srcDevice);
KUndo2Command *cmd = device->convertTo(m_d->paintingColorSpace);
delete cmd;
}
if (!m_d->useOcio()) {
return device->convertToQImage(m_d->monitorProfile, m_d->renderingIntent, m_d->conversionFlags);
} else {
if (m_d->displayFilter->useInternalColorManagement()) {
if (device == srcDevice) {
device = new KisPaintDevice(*srcDevice);
}
const KoColorSpace *srcCS =
KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
m_d->monitorProfile);
KUndo2Command *cmd = device->convertTo(srcCS, m_d->renderingIntent, m_d->conversionFlags);
delete cmd;
}
return m_d->finalIsRgba(device->colorSpace()) ?
m_d->convertToQImageDirect<true>(device) :
m_d->convertToQImageDirect<false>(device);
}
return QImage();
}
KoColor KisDisplayColorConverter::Private::approximateFromQColor(const QColor &qcolor)
{
if (!useOcio()) {
return KoColor(qcolor, paintingColorSpace);
} else {
KoColor color(qcolor, intermediateColorSpace);
displayFilter->approximateInverseTransformation(color.data(), 1);
color.convertTo(paintingColorSpace);
return color;
}
qFatal("Must not be reachable");
return KoColor();
}
QColor KisDisplayColorConverter::Private::approximateToQColor(const KoColor &srcColor)
{
KoColor color(srcColor);
if (useOcio()) {
color.convertTo(intermediateColorSpace);
displayFilter->approximateForwardTransformation(color.data(), 1);
}
return color.toQColor();
}
KoColor KisDisplayColorConverter::fromHsv(int h, int s, int v, int a) const
{
// generate HSV from sRGB!
QColor qcolor(QColor::fromHsv(h, s, v, a));
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsv(const KoColor &srcColor, int *h, int *s, int *v, int *a) const
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHsv(h, s, v, a);
}
KoColor KisDisplayColorConverter::fromHsvF(qreal h, qreal s, qreal v, qreal a)
{
// generate HSV from sRGB!
QColor qcolor(QColor::fromHsvF(h, s, v, a));
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsvF(const KoColor &srcColor, qreal *h, qreal *s, qreal *v, qreal *a)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHsvF(h, s, v, a);
}
KoColor KisDisplayColorConverter::fromHslF(qreal h, qreal s, qreal l, qreal a)
{
// generate HSL from sRGB!
QColor qcolor(QColor::fromHslF(h, s, l, a));
if (!qcolor.isValid()) {
- qWarning() << "Could not construct valid color from h" << h << "s" << s << "l" << l << "a" << a;
+ warnKrita << "Could not construct valid color from h" << h << "s" << s << "l" << l << "a" << a;
qcolor = Qt::black;
}
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHslF(const KoColor &srcColor, qreal *h, qreal *s, qreal *l, qreal *a)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHslF(h, s, l, a);
}
KoColor KisDisplayColorConverter::fromHsiF(qreal h, qreal s, qreal i)
{
// generate HSI from sRGB!
qreal r=0.0;
qreal g=0.0;
qreal b=0.0;
qreal a=1.0;
HSIToRGB(h, s, i, &r, &g, &b);
QColor qcolor;
qcolor.setRgbF(r, g, b, a);
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsiF(const KoColor &srcColor, qreal *h, qreal *s, qreal *i)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
qreal r=color.redF();
qreal g=color.greenF();
qreal b=color.blueF();
RGBToHSI(r, g, b, h, s, i);
}
KoColor KisDisplayColorConverter::fromHsyF(qreal h, qreal s, qreal y, qreal R, qreal G, qreal B)
{
// generate HSL from sRGB!
qreal r=0.0;
qreal g=0.0;
qreal b=0.0;
qreal a=1.0;
HSYToRGB(h, s, y, &r, &g, &b, R, G, B);
QColor qcolor;
qcolor.setRgbF(r, g, b, a);
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsyF(const KoColor &srcColor, qreal *h, qreal *s, qreal *y, qreal R, qreal G, qreal B)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
qreal r=color.redF();
qreal g=color.greenF();
qreal b=color.blueF();
RGBToHSY(r, g, b, h, s, y, R, G, B);
}
#include "moc_kis_display_color_converter.cpp"
diff --git a/krita/ui/canvas/kis_perspective_grid_decoration.cpp b/krita/ui/canvas/kis_perspective_grid_decoration.cpp
index 5b80e1998be..220fc5e8628 100644
--- a/krita/ui/canvas/kis_perspective_grid_decoration.cpp
+++ b/krita/ui/canvas/kis_perspective_grid_decoration.cpp
@@ -1,253 +1,253 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006,2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2014 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_perspective_grid_decoration.h"
#include <QPainter>
#include <klocale.h>
#include "kis_perspective_grid.h"
#include "canvas/kis_grid_painter_configuration.h"
#include "KisView.h"
/***************************************************************/
/* Auxiliary line structures */
/***************************************************************/
class KisPerspectiveGridDecoration::LineWrapper {
public:
LineWrapper(const QPointF &p0, const QPointF &p1) {
init(toKisVector2D(p0), toKisVector2D(p1));
}
LineWrapper(const KisVector2D &p0, const KisVector2D &p1) {
init(p0, p1);
}
QPointF intersects(const LineWrapper &other)/* const */{
KisVector2D n0 = m_lineEquation.normal();
KisVector2D n1 = other.m_lineEquation.normal();
// Ensure vectors have the same direction
if((n0(0) > 0) != (n1(0) > 0)) {
n1 = -n1;
}
if(qFuzzyCompare(n0(0), n1(0)) &&
qFuzzyCompare(n0(1), n1(1))) {
const KisVector2D nearPoint(0,0);
const KisVector2D farPoint(1e10,1e10);
KisVector2D otherPt = other.m_lineEquation.projection(nearPoint);
KisVector2D otherPtProj = m_lineEquation.projection(otherPt);
KisVector2D newOffset = otherPt + 0.5 * (otherPtProj - otherPt);
LineEquation tempLine(n0, newOffset);
// Just throw it somewhere towards infinity...
return toQPointF(tempLine.projection(farPoint));
}
return toQPointF(m_lineEquation.intersection(other.m_lineEquation));
}
qreal distance(const QPointF &pt) const {
return m_lineEquation.absDistance(toKisVector2D(pt));
}
QPointF p0() const {
return m_p0;
}
QPointF p1() const {
return m_p1;
}
bool contains(const QPointF &pt) const {
bool coincide = pt == m_p0 || pt == m_p1;
bool verticalSide = (pt.x() > m_p0.x()) == (pt.x() > m_p1.x());
bool horizontalSide = (pt.y() > m_p0.y()) == (pt.y() > m_p1.y());
return coincide || !(verticalSide && horizontalSide);
}
bool isNull(qreal precision) {
qreal dx = m_p1.x() - m_p0.x();
qreal dy = m_p1.y() - m_p0.y();
return dx * dx + dy * dy <= precision * precision;
}
private:
void init(const KisVector2D &p0, const KisVector2D &p1) {
m_lineEquation =
LineEquation::Through(p0, p1);
m_p0 = toQPointF(p0);
m_p1 = toQPointF(p1);
}
private:
LineEquation m_lineEquation;
QPointF m_p0;
QPointF m_p1;
};
struct KisPerspectiveGridDecoration::SubdivisionLinesInfo {
QPointF startPoint;
QPointF shift;
QPointF intersection;
const LineWrapper *clipLine;
int numSubdivisions;
};
KisPerspectiveGridDecoration::KisPerspectiveGridDecoration(QPointer<KisView> parent)
: KisCanvasDecoration("perspectiveGrid", parent)
{
}
KisPerspectiveGridDecoration::~KisPerspectiveGridDecoration()
{
}
KisPerspectiveGridDecoration::SubdivisionLinesInfo
KisPerspectiveGridDecoration::getSubdivisionsInfo(const LineWrapper &l0,
const LineWrapper &l1,
const QPointF &focusPoint,
int numSubdivisions)
{
const LineWrapper *nearest;
const LineWrapper *farthest;
if(l0.distance(focusPoint) < l1.distance(focusPoint)) {
nearest = &l0;
farthest = &l1;
} else {
nearest = &l1;
farthest = &l0;
}
SubdivisionLinesInfo info;
info.startPoint = farthest->p0();
info.shift = (farthest->p1() - farthest->p0()) / numSubdivisions;
info.intersection = focusPoint;
info.clipLine = nearest;
info.numSubdivisions = numSubdivisions;
return info;
}
void KisPerspectiveGridDecoration::drawSubdivisions(QPainter& gc, const SubdivisionLinesInfo &info)
{
- kDebug() << " subdivs " << info.numSubdivisions;
+ dbgKrita << " subdivs " << info.numSubdivisions;
for(int i = info.numSubdivisions - 1; i > 0; i--) {
QPointF start = info.startPoint + i*info.shift;
QPointF end =
LineWrapper(start, info.intersection).intersects(*info.clipLine);
gc.drawLine(start, end);
}
}
#define SMALLEST_LINE 1e-10
void KisPerspectiveGridDecoration::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas)
{
Q_UNUSED(updateArea);
Q_UNUSED(canvas);
if (!view()) return;
KisImageWSP image = view()->image();
if (!image) return;
KisPerspectiveGrid* pGrid = image->perspectiveGrid();
QPen mainPen = KisGridPainterConfiguration::mainPen();
QPen subdivisionPen = KisGridPainterConfiguration::subdivisionPen();
QPen errorPen = mainPen;
errorPen.setColor(Qt::red);
QTransform transform = converter->imageToWidgetTransform();
gc.save();
gc.setTransform(transform);
for (QList<KisSubPerspectiveGrid*>::const_iterator it = pGrid->begin(); it != pGrid->end(); ++it) {
const KisSubPerspectiveGrid* grid = *it;
/**
* Note that the notion of top-bottom-right-left
* is purely theorical
*/
LineWrapper lineTop(*grid->topLeft(), *grid->topRight());
LineWrapper lineRight(*grid->topRight(), *grid->bottomRight());
LineWrapper lineBottom(*grid->bottomRight(), *grid->bottomLeft());
LineWrapper lineLeft(*grid->bottomLeft(), *grid->topLeft());
QPointF horizIntersection;
QPointF vertIntersection;
bool linesNotNull = true;
bool polygonIsConvex = true;
if(lineTop.isNull(SMALLEST_LINE) ||
lineBottom.isNull(SMALLEST_LINE) ||
lineLeft.isNull(SMALLEST_LINE) ||
lineRight.isNull(SMALLEST_LINE)) {
linesNotNull = false;
}
if(linesNotNull) {
horizIntersection = lineTop.intersects(lineBottom);
vertIntersection = lineLeft.intersects(lineRight);
if(lineTop.contains(horizIntersection) ||
lineBottom.contains(horizIntersection) ||
lineLeft.contains(vertIntersection) ||
lineRight.contains(vertIntersection)) {
polygonIsConvex = false;
}
}
if(polygonIsConvex && linesNotNull) {
gc.setPen(subdivisionPen);
SubdivisionLinesInfo info;
info = getSubdivisionsInfo(lineTop, lineBottom, vertIntersection,
grid->subdivisions());
drawSubdivisions(gc, info);
info = getSubdivisionsInfo(lineLeft, lineRight, horizIntersection,
grid->subdivisions());
drawSubdivisions(gc, info);
}
gc.setPen(polygonIsConvex && linesNotNull ? mainPen : errorPen);
gc.drawLine(*grid->topLeft(), *grid->topRight());
gc.drawLine(*grid->topRight(), *grid->bottomRight());
gc.drawLine(*grid->bottomRight(), *grid->bottomLeft());
gc.drawLine(*grid->bottomLeft(), *grid->topLeft());
}
gc.restore();
}
diff --git a/krita/ui/canvas/kis_prescaled_projection.cpp b/krita/ui/canvas/kis_prescaled_projection.cpp
index 51c9d2b525f..1e51fa95455 100644
--- a/krita/ui/canvas/kis_prescaled_projection.cpp
+++ b/krita/ui/canvas/kis_prescaled_projection.cpp
@@ -1,400 +1,400 @@
/*
* Copyright (c) 2007, Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008, Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2009, Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_prescaled_projection.h"
#include <math.h>
#include <QImage>
#include <QColor>
#include <QRect>
#include <QPoint>
#include <QSize>
#include <QPainter>
#include <KoColorProfile.h>
#include <KoViewConverter.h>
#include "kis_config.h"
#include "kis_image_config.h"
#include "kis_config_notifier.h"
#include "kis_image.h"
#include "krita_utils.h"
#include "kis_coordinates_converter.h"
#include "kis_projection_backend.h"
#include "kis_image_pyramid.h"
#include "kis_display_filter.h"
#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
inline void copyQImageBuffer(uchar* dst, const uchar* src , qint32 deltaX, qint32 width)
{
if (deltaX >= 0) {
memcpy(dst + 4 * deltaX, src, 4 *(width - deltaX) * sizeof(uchar));
} else {
memcpy(dst, src - 4 * deltaX, 4 *(width + deltaX) * sizeof(uchar));
}
}
void copyQImage(qint32 deltaX, qint32 deltaY, QImage* dstImage, const QImage& srcImage)
{
qint32 height = dstImage->height();
qint32 width = dstImage->width();
Q_ASSERT(dstImage->width() == srcImage.width() && dstImage->height() == srcImage.height());
if (deltaY >= 0) {
for (int y = 0; y < height - deltaY; y ++) {
const uchar* src = srcImage.scanLine(y);
uchar* dst = dstImage->scanLine(y + deltaY);
copyQImageBuffer(dst, src, deltaX, width);
}
} else {
for (int y = 0; y < height + deltaY; y ++) {
const uchar* src = srcImage.scanLine(y - deltaY);
uchar* dst = dstImage->scanLine(y);
copyQImageBuffer(dst, src, deltaX, width);
}
}
}
struct KisPrescaledProjection::Private {
Private()
: viewportSize(0, 0)
, projectionBackend(0) {
}
QImage prescaledQImage;
QSize updatePatchSize;
QSize canvasSize;
QSize viewportSize;
KisImageWSP image;
KisCoordinatesConverter *coordinatesConverter;
KisProjectionBackend* projectionBackend;
};
KisPrescaledProjection::KisPrescaledProjection()
: QObject(0)
, m_d(new Private())
{
updateSettings();
// we disable building the pyramid with setting its height to 1
// XXX: setting it higher than 1 is broken because it's not updated until you show/hide the layer
m_d->projectionBackend = new KisImagePyramid(1);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(updateSettings()));
}
KisPrescaledProjection::~KisPrescaledProjection()
{
delete m_d->projectionBackend;
delete m_d;
}
void KisPrescaledProjection::setImage(KisImageWSP image)
{
Q_ASSERT(image);
m_d->image = image;
m_d->projectionBackend->setImage(image);
}
QImage KisPrescaledProjection::prescaledQImage() const
{
return m_d->prescaledQImage;
}
void KisPrescaledProjection::setCoordinatesConverter(KisCoordinatesConverter *coordinatesConverter)
{
m_d->coordinatesConverter = coordinatesConverter;
}
void KisPrescaledProjection::updateSettings()
{
KisImageConfig imageConfig;
m_d->updatePatchSize.setWidth(imageConfig.updatePatchWidth());
m_d->updatePatchSize.setHeight(imageConfig.updatePatchHeight());
}
void KisPrescaledProjection::viewportMoved(const QPointF &offset)
{
// FIXME: \|/
if (m_d->prescaledQImage.isNull()) return;
if (offset.isNull()) return;
QPoint alignedOffset = offset.toPoint();
if(offset != alignedOffset) {
/**
* We can't optimize anything when offset is float :(
* Just prescale entire image.
*/
dbgRender << "prescaling the entire image because the offset is float";
preScale();
return;
}
QImage newImage = QImage(m_d->viewportSize, QImage::Format_ARGB32);
newImage.fill(0);
/**
* TODO: viewport rects should be cropped by the borders of
* the image, because it may be requested to read/write
* outside QImage and copyQImage will not catch it
*/
QRect newViewportRect = QRect(QPoint(0,0), m_d->viewportSize);
QRect oldViewportRect = newViewportRect.translated(alignedOffset);
QRegion updateRegion = newViewportRect;
QRect savedArea = newViewportRect & oldViewportRect;
if(!savedArea.isEmpty()) {
copyQImage(alignedOffset.x(), alignedOffset.y(), &newImage, m_d->prescaledQImage);
updateRegion -= savedArea;
}
QPainter gc(&newImage);
QVector<QRect> rects = updateRegion.rects();
foreach(const QRect &rect, rects) {
QRect imageRect =
m_d->coordinatesConverter->viewportToImage(rect).toAlignedRect();
QVector<QRect> patches =
KritaUtils::splitRectIntoPatches(imageRect, m_d->updatePatchSize);
foreach(const QRect& rc, patches) {
QRect viewportPatch =
m_d->coordinatesConverter->imageToViewport(rc).toAlignedRect();
KisPPUpdateInfoSP info = getInitialUpdateInformation(QRect());
fillInUpdateInformation(viewportPatch, info);
drawUsingBackend(gc, info);
}
}
m_d->prescaledQImage = newImage;
}
void KisPrescaledProjection::slotImageSizeChanged(qint32 w, qint32 h)
{
m_d->projectionBackend->setImageSize(w, h);
// viewport size is cropped by the size of the image
// so we need to update it as well
updateViewportSize();
}
KisUpdateInfoSP KisPrescaledProjection::updateCache(const QRect &dirtyImageRect)
{
if (!m_d->image) {
- dbgRender << "Calling updateCache without an image: " << kBacktrace() << endl;
+ dbgRender << "Calling updateCache without an image: " << kisBacktrace() << endl;
// return invalid info
return new KisPPUpdateInfo();
}
/**
* We needn't this stuff ouside KisImage's area. We're not displaying
* anything painted outside the image anyway.
*/
QRect croppedImageRect = dirtyImageRect & m_d->image->bounds();
if (croppedImageRect.isEmpty()) return new KisPPUpdateInfo();
KisPPUpdateInfoSP info = getInitialUpdateInformation(croppedImageRect);
m_d->projectionBackend->updateCache(croppedImageRect);
return info;
}
void KisPrescaledProjection::recalculateCache(KisUpdateInfoSP info)
{
KisPPUpdateInfoSP ppInfo = dynamic_cast<KisPPUpdateInfo*>(info.data());
if(!ppInfo) return;
QRect rawViewRect =
m_d->coordinatesConverter->
imageToViewport(ppInfo->dirtyImageRectVar).toAlignedRect();
fillInUpdateInformation(rawViewRect, ppInfo);
m_d->projectionBackend->recalculateCache(ppInfo);
if(!info->dirtyViewportRect().isEmpty())
updateScaledImage(ppInfo);
}
void KisPrescaledProjection::preScale()
{
if (!m_d->image) return;
m_d->prescaledQImage.fill(0);
QRect viewportRect(QPoint(0, 0), m_d->viewportSize);
QRect imageRect =
m_d->coordinatesConverter->viewportToImage(viewportRect).toAlignedRect();
QVector<QRect> patches =
KritaUtils::splitRectIntoPatches(imageRect, m_d->updatePatchSize);
foreach(const QRect& rc, patches) {
QRect viewportPatch = m_d->coordinatesConverter->imageToViewport(rc).toAlignedRect();
KisPPUpdateInfoSP info = getInitialUpdateInformation(QRect());
fillInUpdateInformation(viewportPatch, info);
QPainter gc(&m_d->prescaledQImage);
gc.setCompositionMode(QPainter::CompositionMode_Source);
drawUsingBackend(gc, info);
}
}
void KisPrescaledProjection::setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
m_d->projectionBackend->setMonitorProfile(monitorProfile, renderingIntent, conversionFlags);
}
void KisPrescaledProjection::setChannelFlags(const QBitArray &channelFlags)
{
m_d->projectionBackend->setChannelFlags(channelFlags);
}
void KisPrescaledProjection::setDisplayFilter(KisDisplayFilter* displayFilter)
{
m_d->projectionBackend->setDisplayFilter(displayFilter);
}
void KisPrescaledProjection::updateViewportSize()
{
QRectF imageRect = m_d->coordinatesConverter->imageRectInWidgetPixels();
QSizeF minimalSize(qMin(imageRect.width(), (qreal)m_d->canvasSize.width()),
qMin(imageRect.height(), (qreal)m_d->canvasSize.height()));
QRectF minimalRect(QPointF(0,0), minimalSize);
m_d->viewportSize = m_d->coordinatesConverter->widgetToViewport(minimalRect).toAlignedRect().size();
if (m_d->prescaledQImage.isNull() ||
m_d->prescaledQImage.size() != m_d->viewportSize) {
m_d->prescaledQImage = QImage(m_d->viewportSize, QImage::Format_ARGB32);
m_d->prescaledQImage.fill(0);
}
}
void KisPrescaledProjection::notifyZoomChanged()
{
updateViewportSize();
preScale();
}
void KisPrescaledProjection::notifyCanvasSizeChanged(const QSize &widgetSize)
{
m_d->canvasSize = widgetSize;
updateViewportSize();
preScale();
}
KisPPUpdateInfoSP KisPrescaledProjection::getInitialUpdateInformation(const QRect &dirtyImageRect)
{
/**
* This update information has nothing more than an information
* about dirty image rect. All the other information used for
* scaling will be fetched in fillUpdateInformation() later,
* when we are working in the context of the UI thread
*/
KisPPUpdateInfoSP info = new KisPPUpdateInfo();
info->dirtyImageRectVar = dirtyImageRect;
return info;
}
void KisPrescaledProjection::fillInUpdateInformation(const QRect &viewportRect,
KisPPUpdateInfoSP info)
{
m_d->coordinatesConverter->imageScale(&info->scaleX, &info->scaleY);
// first, crop the part of the view rect that is outside of the canvas
QRect croppedViewRect = viewportRect.intersected(QRect(QPoint(0, 0), m_d->viewportSize));
// second, align this rect to the KisImage's pixels and pixels
// of projection backend.
info->imageRect = m_d->coordinatesConverter->viewportToImage(QRectF(croppedViewRect)).toAlignedRect();
/**
* To avoid artifacts while scaling we use machanism like
* changeRect/needRect for layers. Here we grow the rect to update
* pixels which depend on the dirty rect (like changeRect), and
* later we request a bit more pixels for the patch to make the
* scaling safe (like needRect).
*/
const int borderSize = BORDER_SIZE(qMax(info->scaleX, info->scaleY));
info->imageRect.adjust(-borderSize, -borderSize, borderSize, borderSize);
info->imageRect = info->imageRect & m_d->image->bounds();
m_d->projectionBackend->alignSourceRect(info->imageRect, info->scaleX);
// finally, compute the dirty rect of the canvas
info->viewportRect = m_d->coordinatesConverter->imageToViewport(info->imageRect);
info->borderWidth = 0;
if (SCALE_MORE_OR_EQUAL_TO(info->scaleX, info->scaleY, 1.0)) {
if (SCALE_LESS_THAN(info->scaleX, info->scaleY, 2.0)) {
dbgRender << "smoothBetween100And200Percent" << endl;
info->renderHints = QPainter::SmoothPixmapTransform;
info->borderWidth = borderSize;
}
info->transfer = KisPPUpdateInfo::DIRECT;
} else { // <100%
info->renderHints = QPainter::SmoothPixmapTransform;
info->borderWidth = borderSize;
info->transfer = KisPPUpdateInfo::PATCH;
}
dbgRender << "#####################################";
dbgRender << ppVar(info->scaleX) << ppVar(info->scaleY);
dbgRender << ppVar(info->borderWidth) << ppVar(info->renderHints);
dbgRender << ppVar(info->transfer);
dbgRender << ppVar(info->dirtyImageRectVar);
dbgRender << "Not aligned rect of the canvas (raw):\t" << croppedViewRect;
dbgRender << "Update rect in KisImage's pixels:\t" << info->imageRect;
dbgRender << "Update rect in canvas' pixels:\t" << info->viewportRect;
dbgRender << "#####################################";
}
void KisPrescaledProjection::updateScaledImage(KisPPUpdateInfoSP info)
{
QPainter gc(&m_d->prescaledQImage);
gc.setCompositionMode(QPainter::CompositionMode_Source);
drawUsingBackend(gc, info);
}
void KisPrescaledProjection::drawUsingBackend(QPainter &gc, KisPPUpdateInfoSP info)
{
if (info->imageRect.isEmpty()) return;
if (info->transfer == KisPPUpdateInfo::DIRECT) {
m_d->projectionBackend->drawFromOriginalImage(gc, info);
} else /* if info->transfer == KisPPUpdateInformation::PATCH */ {
KisImagePatch patch = m_d->projectionBackend->getNearestPatch(info);
// prescale the patch because otherwise we'd scale using QPainter, which gives
// a crap result compared to QImage's smoothscale
patch.preScale(info->viewportRect);
patch.drawMe(gc, info->viewportRect, info->renderHints);
}
}
diff --git a/krita/ui/canvas/kis_qpainter_canvas.cpp b/krita/ui/canvas/kis_qpainter_canvas.cpp
index 9e6e23a0b04..e6698bdb736 100644
--- a/krita/ui/canvas/kis_qpainter_canvas.cpp
+++ b/krita/ui/canvas/kis_qpainter_canvas.cpp
@@ -1,254 +1,254 @@
/*
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006
* Copyright (C) Lukas Tvrdy <lukast.dev@gmail.com>, (C) 2009
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_qpainter_canvas.h"
#include <QPaintEvent>
#include <QPoint>
#include <QRect>
#include <QPainter>
#include <QImage>
#include <QBrush>
#include <QColor>
#include <QString>
#include <QTime>
#include <QTimer>
#include <QApplication>
#include <QMenu>
#include <kis_debug.h>
#include <KoColorProfile.h>
#include "kis_coordinates_converter.h"
#include <KoZoomHandler.h>
#include <KoToolManager.h>
#include <KoToolProxy.h>
#include <kis_image.h>
#include <kis_layer.h>
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include "kis_prescaled_projection.h"
#include "kis_config.h"
#include "kis_canvas_resource_provider.h"
#include "KisDocument.h"
#include "kis_selection_manager.h"
#include "kis_selection.h"
#include "kis_perspective_grid_manager.h"
#include "kis_canvas_updates_compressor.h"
#include "kis_config_notifier.h"
#include "kis_group_layer.h"
#include "canvas/kis_display_color_converter.h"
//#define DEBUG_REPAINT
#include <KoCanvasController.h>
class KisQPainterCanvas::Private
{
public:
KisPrescaledProjectionSP prescaledProjection;
QBrush checkBrush;
};
KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent)
: QWidget(parent)
, KisCanvasWidgetBase(canvas, coordinatesConverter)
, m_d(new Private())
{
setAutoFillBackground(true);
setAcceptDrops(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_OpaquePaintEvent);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
}
KisQPainterCanvas::~KisQPainterCanvas()
{
delete m_d;
}
void KisQPainterCanvas::setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection)
{
m_d->prescaledProjection = prescaledProjection;
}
void KisQPainterCanvas::paintEvent(QPaintEvent * ev)
{
KisImageWSP image = canvas()->image();
if (image == 0) return;
setAutoFillBackground(false);
if (m_buffer.size() != size()) {
m_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
}
QPainter gc(&m_buffer);
// we double buffer, so we paint on an image first, then from the image onto the canvas,
// so copy the clip region since otherwise we're filling the whole buffer every time with
// the background color _and_ the transparent squares.
gc.setClipRegion(ev->region());
KisCoordinatesConverter *converter = coordinatesConverter();
QTransform imageTransform = converter->viewportToWidgetTransform();
gc.save();
gc.setCompositionMode(QPainter::CompositionMode_Source);
gc.fillRect(QRect(QPoint(0, 0), size()), borderColor());
QTransform checkersTransform;
QPointF brushOrigin;
QPolygonF polygon;
converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon);
gc.setPen(Qt::NoPen);
gc.setBrush(m_d->checkBrush);
gc.setBrushOrigin(brushOrigin);
gc.setTransform(checkersTransform);
gc.drawPolygon(polygon);
gc.setTransform(imageTransform);
gc.setRenderHint(QPainter::SmoothPixmapTransform, true);
QRectF viewportRect = converter->widgetToViewport(ev->rect());
gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(),
viewportRect);
gc.restore();
#ifdef DEBUG_REPAINT
QColor color = QColor(random() % 255, random() % 255, random() % 255, 150);
gc.fillRect(ev->rect(), color);
#endif
drawDecorations(gc, ev->rect());
gc.end();
QPainter painter(this);
painter.drawImage(ev->rect(), m_buffer, ev->rect());
}
QVariant KisQPainterCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
{
return processInputMethodQuery(query);
}
void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event)
{
processInputMethodEvent(event);
}
void KisQPainterCanvas::channelSelectionChanged(QBitArray channelFlags)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setChannelFlags(channelFlags);
}
void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisQPainterCanvas::setDisplayFilter(KisDisplayFilter* displayFilter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setDisplayFilter(displayFilter);
canvas()->startUpdateInPatches(canvas()->image()->bounds());
}
void KisQPainterCanvas::setWrapAroundViewingMode(bool value)
{
- kDebug() << "Wrap around viewing mode not implemented in QPainter Canvas.";
+ dbgKrita << "Wrap around viewing mode not implemented in QPainter Canvas.";
return;
}
void KisQPainterCanvas::disconnectCurrentCanvas()
{ }
void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h)
{
m_d->prescaledProjection->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags)
{
Q_UNUSED(channelFlags);
return m_d->prescaledProjection->updateCache(rc);
}
QRect KisQPainterCanvas::updateCanvasProjection(KisUpdateInfoSP info)
{
/**
* It might happen that the canvas type is switched while the
* update info is being stuck in the Qt's signals queue. Than a wrong
* type of the info may come. So just check it here.
*/
bool isPPUpdateInfo = dynamic_cast<KisPPUpdateInfo*>(info.data());
if (isPPUpdateInfo) {
m_d->prescaledProjection->recalculateCache(info);
return info->dirtyViewportRect();
} else {
return QRect();
}
}
void KisQPainterCanvas::resizeEvent(QResizeEvent *e)
{
QSize size(e->size());
if (size.width() <= 0) {
size.setWidth(1);
}
if (size.height() <= 0) {
size.setHeight(1);
}
coordinatesConverter()->setCanvasWidgetSize(size);
m_d->prescaledProjection->notifyCanvasSizeChanged(size);
}
void KisQPainterCanvas::slotConfigChanged()
{
m_d->checkBrush = QBrush(createCheckersImage());
notifyConfigChanged();
}
bool KisQPainterCanvas::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
diff --git a/krita/ui/canvas/kis_tool_proxy.cpp b/krita/ui/canvas/kis_tool_proxy.cpp
index 580524ae571..9754d5ce8f9 100644
--- a/krita/ui/canvas/kis_tool_proxy.cpp
+++ b/krita/ui/canvas/kis_tool_proxy.cpp
@@ -1,239 +1,244 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_proxy.h"
#include "kis_canvas2.h"
+#include "input/kis_tablet_debugger.h"
#include <KoToolProxy_p.h>
KisToolProxy::KisToolProxy(KoCanvasBase *canvas, QObject *parent)
: KoToolProxy(canvas, parent),
m_isActionActivated(false),
m_lastAction(KisTool::Primary)
{
}
QPointF KisToolProxy::tabletToDocument(const QPointF &globalPos)
{
const QPointF pos = globalPos - canvas()->canvasWidget()->mapToGlobal(QPoint(0, 0));
return widgetToDocument(pos);
}
QPointF KisToolProxy::widgetToDocument(const QPointF &widgetPoint) const
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->coordinatesConverter()->widgetToDocument(widgetPoint);
}
KoPointerEvent KisToolProxy::convertEventToPointerEvent(QEvent *event, const QPointF &docPoint, bool *result)
{
switch (event->type()) {
case QEvent::TabletPress:
case QEvent::TabletRelease:
case QEvent::TabletMove:
{
*result = true;
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
KoPointerEvent ev(tabletEvent, docPoint);
ev.setTabletButton(Qt::LeftButton);
return ev;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
{
*result = true;
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
return KoPointerEvent(mouseEvent, docPoint);
}
default:
;
}
*result = false;
QMouseEvent fakeEvent(QEvent::MouseMove, QPoint(),
Qt::NoButton, Qt::NoButton,
Qt::NoModifier);
return KoPointerEvent(&fakeEvent, QPointF());
}
-void KisToolProxy::forwardMouseHoverEvent(QMouseEvent *mouseEvent, QTabletEvent *lastTabletEvent)
+void KisToolProxy::forwardHoverEvent(QEvent *event)
{
- if (lastTabletEvent) {
- QPointF docPoint = tabletToDocument(lastTabletEvent->hiResGlobalPos());
- this->tabletEvent(lastTabletEvent, docPoint);
- } else {
- KIS_ASSERT_RECOVER_RETURN(mouseEvent->type() == QEvent::MouseMove);
+ switch (event->type()) {
+ case QEvent::TabletMove: {
+ QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
+ QPointF docPoint = widgetToDocument(tabletEvent->posF());
+ this->tabletEvent(tabletEvent, docPoint);
+ return;
+ }
+
+ case QEvent::MouseMove: {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QPointF docPoint = widgetToDocument(mouseEvent->posF());
mouseMoveEvent(mouseEvent, docPoint);
+ return;
+ }
+
+ default: {
+ qWarning() << "forwardHoverEvent encountered unknown event type.";
+ return;
+ }
}
}
-bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent, QTabletEvent *lastTabletEvent)
+bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent)
{
bool retval = true;
QTabletEvent *tabletEvent = dynamic_cast<QTabletEvent*>(event);
QTouchEvent *touchEvent = dynamic_cast<QTouchEvent*>(event);
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (tabletEvent) {
- QPointF docPoint = tabletToDocument(tabletEvent->hiResGlobalPos());
+ QPointF docPoint = tabletToDocument(tabletEvent->globalPosF());
tabletEvent->accept();
this->tabletEvent(tabletEvent, docPoint);
forwardToTool(state, action, tabletEvent, docPoint);
retval = tabletEvent->isAccepted();
} else if (touchEvent) {
if (state == END && touchEvent->type() != QEvent::TouchEnd) {
//Fake a touch end if we are "upgrading" a single-touch gesture to a multi-touch gesture.
QTouchEvent fakeEvent(QEvent::TouchEnd, touchEvent->device(),
touchEvent->modifiers(), touchEvent->touchPointStates(),
touchEvent->touchPoints());
this->touchEvent(&fakeEvent);
} else {
this->touchEvent(touchEvent);
}
} else if (mouseEvent) {
- if (lastTabletEvent) {
- QPointF docPoint = tabletToDocument(lastTabletEvent->hiResGlobalPos());
- lastTabletEvent->accept();
- this->tabletEvent(lastTabletEvent, docPoint);
- forwardToTool(state, action, lastTabletEvent, docPoint);
- retval = lastTabletEvent->isAccepted();
- } else {
- QPointF docPoint = widgetToDocument(mouseEvent->posF());
- mouseEvent->accept();
- if (mouseEvent->type() == QEvent::MouseButtonPress) {
- mousePressEvent(mouseEvent, docPoint);
- } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) {
- mouseDoubleClickEvent(mouseEvent, docPoint);
- } else if (mouseEvent->type() == QEvent::MouseButtonRelease) {
- mouseReleaseEvent(mouseEvent, docPoint);
- } else if (mouseEvent->type() == QEvent::MouseMove) {
- mouseMoveEvent(mouseEvent, docPoint);
- }
- forwardToTool(state, action, originalEvent, docPoint);
- retval = mouseEvent->isAccepted();
+ QPointF docPoint = widgetToDocument(mouseEvent->posF());
+ mouseEvent->accept();
+ if (mouseEvent->type() == QEvent::MouseButtonPress) {
+ mousePressEvent(mouseEvent, docPoint);
+ } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) {
+ mouseDoubleClickEvent(mouseEvent, docPoint);
+ } else if (mouseEvent->type() == QEvent::MouseButtonRelease) {
+ mouseReleaseEvent(mouseEvent, docPoint);
+ } else if (mouseEvent->type() == QEvent::MouseMove) {
+ mouseMoveEvent(mouseEvent, docPoint);
}
+ forwardToTool(state, action, originalEvent, docPoint);
+ retval = mouseEvent->isAccepted();
} else if(event->type() == QEvent::KeyPress) {
QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
keyPressEvent(kevent);
} else if(event->type() == QEvent::KeyRelease) {
QKeyEvent* kevent = static_cast<QKeyEvent*>(event);
keyReleaseEvent(kevent);
}
return retval;
}
void KisToolProxy::forwardToTool(ActionState state, KisTool::ToolAction action, QEvent *event, const QPointF &docPoint)
{
bool eventValid = false;
KoPointerEvent ev = convertEventToPointerEvent(event, docPoint, &eventValid);
KisTool *activeTool = dynamic_cast<KisTool*>(priv()->activeTool);
if (!eventValid || !activeTool) return;
switch (state) {
case BEGIN:
if (action == KisTool::Primary) {
if (event->type() == QEvent::MouseButtonDblClick) {
activeTool->beginPrimaryDoubleClickAction(&ev);
} else {
activeTool->beginPrimaryAction(&ev);
}
} else {
if (event->type() == QEvent::MouseButtonDblClick) {
activeTool->beginAlternateDoubleClickAction(&ev, KisTool::actionToAlternateAction(action));
} else {
activeTool->beginAlternateAction(&ev, KisTool::actionToAlternateAction(action));
}
}
break;
case CONTINUE:
if (action == KisTool::Primary) {
activeTool->continuePrimaryAction(&ev);
} else {
activeTool->continueAlternateAction(&ev, KisTool::actionToAlternateAction(action));
}
break;
case END:
if (action == KisTool::Primary) {
activeTool->endPrimaryAction(&ev);
} else {
activeTool->endAlternateAction(&ev, KisTool::actionToAlternateAction(action));
}
break;
}
}
bool KisToolProxy::primaryActionSupportsHiResEvents() const
{
KisTool *activeTool = dynamic_cast<KisTool*>(const_cast<KisToolProxy*>(this)->priv()->activeTool);
return activeTool && activeTool->primaryActionSupportsHiResEvents();
}
void KisToolProxy::setActiveTool(KoToolBase *tool)
{
if (!tool) return;
if (m_isActionActivated) {
deactivateToolAction(m_lastAction);
KoToolProxy::setActiveTool(tool);
activateToolAction(m_lastAction);
} else {
KoToolProxy::setActiveTool(tool);
}
}
void KisToolProxy::activateToolAction(KisTool::ToolAction action)
{
KisTool *activeTool = dynamic_cast<KisTool*>(const_cast<KisToolProxy*>(this)->priv()->activeTool);
if (activeTool) {
if (action == KisTool::Primary) {
activeTool->activatePrimaryAction();
} else {
activeTool->activateAlternateAction(KisTool::actionToAlternateAction(action));
}
}
m_isActionActivated = true;
m_lastAction = action;
}
void KisToolProxy::deactivateToolAction(KisTool::ToolAction action)
{
KisTool *activeTool = dynamic_cast<KisTool*>(const_cast<KisToolProxy*>(this)->priv()->activeTool);
if (activeTool) {
if (action == KisTool::Primary) {
activeTool->deactivatePrimaryAction();
} else {
activeTool->deactivateAlternateAction(KisTool::actionToAlternateAction(action));
}
}
m_isActionActivated = false;
m_lastAction = action;
}
diff --git a/krita/ui/canvas/kis_tool_proxy.h b/krita/ui/canvas/kis_tool_proxy.h
index 61503cda44f..08ca79dd043 100644
--- a/krita/ui/canvas/kis_tool_proxy.h
+++ b/krita/ui/canvas/kis_tool_proxy.h
@@ -1,71 +1,70 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_PROXY_H
#define __KIS_TOOL_PROXY_H
#include <KoToolProxy.h>
#include <kis_tool.h>
class KisToolProxy : public KoToolProxy
{
public:
enum ActionState {
BEGIN, /**< Beginning an action */
CONTINUE, /**< Continuing an action */
END /**< Ending an action */
};
public:
KisToolProxy(KoCanvasBase *canvas, QObject *parent = 0);
- void forwardMouseHoverEvent(QMouseEvent *mouseEvent, QTabletEvent *lastTabletEvent);
+ void forwardHoverEvent(QEvent *event);
/**
* Forwards the event to the active tool and returns true if the
- * event 'was not ignored'. That is by default the event is
+ * event was not ignored. That is by default the event is
* considered accepted, but the tool can explicitly ignore it.
* @param state beginning, continuing, or ending the action.
* @param action alternate tool action requested.
* @param event the event being sent to the tool by the AbstractInputAction.
* @param originalEvent the original event received by the AbstractInputAction.
- * @param lastTabletEvent The event object for the last tablet event.
*/
- bool forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent, QTabletEvent *lastTabletEvent);
+ bool forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent);
bool primaryActionSupportsHiResEvents() const;
void setActiveTool(KoToolBase *tool);
void activateToolAction(KisTool::ToolAction action);
void deactivateToolAction(KisTool::ToolAction action);
private:
KoPointerEvent convertEventToPointerEvent(QEvent *event, const QPointF &docPoint, bool *result);
QPointF tabletToDocument(const QPointF &globalPos);
void forwardToTool(ActionState state, KisTool::ToolAction action, QEvent *event, const QPointF &docPoint);
protected:
QPointF widgetToDocument(const QPointF &widgetPoint) const;
private:
bool m_isActionActivated;
KisTool::ToolAction m_lastAction;
};
#endif /* __KIS_TOOL_PROXY_H */
diff --git a/krita/ui/dialogs/KisShortcutsEditorItem.cpp b/krita/ui/dialogs/KisShortcutsEditorItem.cpp
index a4ced616a50..91bf680a71d 100644
--- a/krita/ui/dialogs/KisShortcutsEditorItem.cpp
+++ b/krita/ui/dialogs/KisShortcutsEditorItem.cpp
@@ -1,424 +1,424 @@
/* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org>
Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisShortcutsDialog_p.h"
#include <QAction>
#include <QTreeWidgetItem>
-#include <QDebug>
+#include <kis_debug.h>
// KRITAIMPORT: copied here from kshortcutseditor.cpp to be available
//static
KShortcutsEditorItem *KShortcutsEditorPrivate::itemFromIndex(QTreeWidget *const w,
const QModelIndex &index)
{
QTreeWidgetItem *item = static_cast<QTreeWidgetHack *>(w)->itemFromIndex(index);
if (item && item->type() == ActionItem) {
return static_cast<KShortcutsEditorItem *>(item);
}
return 0;
}
#if 0
#include <kgesturemap.h>
#endif
#include <kglobalaccel.h>
KShortcutsEditorItem::KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action)
: QTreeWidgetItem(parent, ActionItem)
, m_action(action)
, m_isNameBold(false)
, m_oldLocalShortcut(0)
, m_oldGlobalShortcut(0)
#if 0
, m_oldShapeGesture(0)
, m_oldRockerGesture(0)
#endif
{
// Filtering message requested by translators (scripting).
m_id = m_action->objectName();
m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration", "%1", KLocalizedString::removeAcceleratorMarker(m_action->text()));
if (m_actionNameInTable.isEmpty()) {
- qWarning() << "Action without text!" << m_action->objectName();
+ warnKrita << "Action without text!" << m_action->objectName();
m_actionNameInTable = m_id;
}
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseSensitive);
}
KShortcutsEditorItem::~KShortcutsEditorItem()
{
delete m_oldLocalShortcut;
delete m_oldGlobalShortcut;
#if 0
delete m_oldShapeGesture;
delete m_oldRockerGesture;
#endif
}
bool KShortcutsEditorItem::isModified() const
{
#if 0
return m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture;
#else
return m_oldLocalShortcut || m_oldGlobalShortcut;
#endif
}
QVariant KShortcutsEditorItem::data(int column, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (column) {
case Name:
return m_actionNameInTable;
case Id:
return m_id;
case LocalPrimary:
case LocalAlternate:
case GlobalPrimary:
case GlobalAlternate:
return qVariantFromValue(keySequence(column));
#if 0
case ShapeGesture:
return qVariantFromValue(KGestureMap::self()->shapeGesture(m_action).shapeName());
case RockerGesture:
return qVariantFromValue(KGestureMap::self()->rockerGesture(m_action).rockerName());
#endif
default:
break;
}
break;
case Qt::DecorationRole:
if (column == Name) {
return m_action->icon();
} else {
return QIcon();
}
break;
case Qt::WhatsThisRole:
return m_action->whatsThis();
case Qt::ToolTipRole:
// There is no such thing as a QAction::description(). So we have
// nothing to display here.
return QVariant();
case Qt::FontRole:
if (column == Name && m_isNameBold) {
QFont modifiedFont = treeWidget()->font();
modifiedFont.setBold(true);
return modifiedFont;
}
break;
case KExtendableItemDelegate::ShowExtensionIndicatorRole:
switch (column) {
case Name:
return false;
case LocalPrimary:
case LocalAlternate:
return !m_action->property("isShortcutConfigurable").isValid()
|| m_action->property("isShortcutConfigurable").toBool();
case GlobalPrimary:
case GlobalAlternate:
if (!KGlobalAccel::self()->hasShortcut(m_action)) {
return false;
}
return true;
default:
return false;
}
//the following are custom roles, defined in this source file only
case ShortcutRole:
switch (column) {
case LocalPrimary:
case LocalAlternate:
case GlobalPrimary:
case GlobalAlternate:
return qVariantFromValue(keySequence(column));
#if 0
case ShapeGesture: { //scoping for "ret"
QVariant ret;
ret.setValue(KGestureMap::self()->shapeGesture(m_action));
return ret;
}
case RockerGesture: {
QVariant ret;
ret.setValue(KGestureMap::self()->rockerGesture(m_action));
return ret;
}
#endif
default:
// Column not valid for this role
Q_ASSERT(false);
return QVariant();
}
case DefaultShortcutRole: {
QList<QKeySequence> defaultShortcuts = m_action->property("defaultShortcuts").value<QList<QKeySequence> >();
QList<QKeySequence> defaultGlobalShortcuts = KGlobalAccel::self()->defaultShortcut(m_action);
switch (column) {
case LocalPrimary:
return qVariantFromValue(primarySequence(defaultShortcuts));
case LocalAlternate:
return qVariantFromValue(alternateSequence(defaultShortcuts));
case GlobalPrimary:
return qVariantFromValue(primarySequence(defaultGlobalShortcuts));
case GlobalAlternate:
return qVariantFromValue(alternateSequence(defaultGlobalShortcuts));
#if 0
case ShapeGesture: {
QVariant ret;
ret.setValue(KGestureMap::self()->defaultShapeGesture(m_action));
return ret;
}
case RockerGesture: {
QVariant ret;
ret.setValue(KGestureMap::self()->defaultRockerGesture(m_action));
return ret;
}
#endif
default:
// Column not valid for this role
Q_ASSERT(false);
return QVariant();
}
}
case ObjectRole:
return qVariantFromValue((QObject *)m_action);
default:
break;
}
return QVariant();
}
bool KShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const
{
const int column = treeWidget() ? treeWidget()->sortColumn() : 0;
return m_collator.compare(text(column), other.text(column)) < 0;
}
QKeySequence KShortcutsEditorItem::keySequence(uint column) const
{
QList<QKeySequence> shortcuts = m_action->shortcuts();
QList<QKeySequence> globalShortcuts = KGlobalAccel::self()->shortcut(m_action);
switch (column) {
case LocalPrimary:
return primarySequence(shortcuts);
case LocalAlternate:
return alternateSequence(shortcuts);
case GlobalPrimary:
return primarySequence(globalShortcuts);
case GlobalAlternate:
return alternateSequence(globalShortcuts);
default:
return QKeySequence();
}
}
void KShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq)
{
QList<QKeySequence> ks;
if (column == GlobalPrimary || column == GlobalAlternate) {
ks = KGlobalAccel::self()->shortcut(m_action);
if (!m_oldGlobalShortcut) {
m_oldGlobalShortcut = new QList<QKeySequence>(ks);
}
} else {
ks = m_action->shortcuts();
if (!m_oldLocalShortcut) {
m_oldLocalShortcut = new QList<QKeySequence>(ks);
}
}
if (column == LocalAlternate || column == GlobalAlternate) {
if (ks.isEmpty()) {
ks << QKeySequence();
}
if (ks.size() <= 1) {
ks << seq;
} else {
ks[1] = seq;
}
} else {
if (ks.isEmpty()) {
ks << seq;
} else {
ks[0] = seq;
}
}
//avoid also setting the default shortcut - what we are setting here is custom by definition
if (column == GlobalPrimary || column == GlobalAlternate) {
KGlobalAccel::self()->setShortcut(m_action, ks, KGlobalAccel::NoAutoloading);
} else {
m_action->setShortcuts(ks);
}
updateModified();
}
#if 0
void KShortcutsEditorItem::setShapeGesture(const KShapeGesture &gst)
{
if (!m_oldShapeGesture) {
m_oldShapeGesture = new KShapeGesture(gst);
}
KGestureMap::self()->setShapeGesture(m_action, gst);
KGestureMap::self()->setDefaultShapeGesture(m_action, gst);
updateModified();
}
#endif
#if 0
void KShortcutsEditorItem::setRockerGesture(const KRockerGesture &gst)
{
if (!m_oldRockerGesture) {
m_oldRockerGesture = new KRockerGesture(gst);
}
KGestureMap::self()->setRockerGesture(m_action, gst);
KGestureMap::self()->setDefaultRockerGesture(m_action, gst);
updateModified();
}
#endif
//our definition of modified is "modified since the chooser was shown".
void KShortcutsEditorItem::updateModified()
{
if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) {
delete m_oldLocalShortcut;
m_oldLocalShortcut = 0;
}
if (m_oldGlobalShortcut && *m_oldGlobalShortcut == KGlobalAccel::self()->shortcut(m_action)) {
delete m_oldGlobalShortcut;
m_oldGlobalShortcut = 0;
}
#if 0
if (m_oldShapeGesture && *m_oldShapeGesture == KGestureMap::self()->shapeGesture(m_action)) {
delete m_oldShapeGesture;
m_oldShapeGesture = 0;
}
if (m_oldRockerGesture && *m_oldRockerGesture == KGestureMap::self()->rockerGesture(m_action)) {
delete m_oldRockerGesture;
m_oldRockerGesture = 0;
}
#endif
}
bool KShortcutsEditorItem::isModified(uint column) const
{
switch (column) {
case Name:
return false;
case LocalPrimary:
case LocalAlternate:
if (!m_oldLocalShortcut) {
return false;
}
if (column == LocalPrimary) {
return primarySequence(*m_oldLocalShortcut) != primarySequence(m_action->shortcuts());
} else {
return alternateSequence(*m_oldLocalShortcut) != alternateSequence(m_action->shortcuts());
}
case GlobalPrimary:
case GlobalAlternate:
if (!m_oldGlobalShortcut) {
return false;
}
if (column == GlobalPrimary) {
return primarySequence(*m_oldGlobalShortcut) != primarySequence(KGlobalAccel::self()->shortcut(m_action));
} else {
return alternateSequence(*m_oldGlobalShortcut) != alternateSequence(KGlobalAccel::self()->shortcut(m_action));
}
#if 0
case ShapeGesture:
return static_cast<bool>(m_oldShapeGesture);
case RockerGesture:
return static_cast<bool>(m_oldRockerGesture);
#endif
default:
return false;
}
}
void KShortcutsEditorItem::undo()
{
#ifndef NDEBUG
#if 0
if (m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture) {
- //qDebug() << "Undoing changes for " << data(Name, Qt::DisplayRole).toString();
+ //dbgKrita << "Undoing changes for " << data(Name, Qt::DisplayRole).toString();
}
#endif
#endif
if (m_oldLocalShortcut) {
// We only ever reset the active Shortcut
m_action->setShortcuts(*m_oldLocalShortcut);
}
if (m_oldGlobalShortcut) {
KGlobalAccel::self()->setShortcut(m_action, *m_oldGlobalShortcut, KGlobalAccel::NoAutoloading);
}
#if 0
if (m_oldShapeGesture) {
KGestureMap::self()->setShapeGesture(m_action, *m_oldShapeGesture);
KGestureMap::self()->setDefaultShapeGesture(m_action, *m_oldShapeGesture);
}
if (m_oldRockerGesture) {
KGestureMap::self()->setRockerGesture(m_action, *m_oldRockerGesture);
KGestureMap::self()->setDefaultRockerGesture(m_action, *m_oldRockerGesture);
}
#endif
updateModified();
}
void KShortcutsEditorItem::commit()
{
#ifndef NDEBUG
#if 0
if (m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture) {
- //qDebug() << "Committing changes for " << data(Name, Qt::DisplayRole).toString();
+ //dbgKrita << "Committing changes for " << data(Name, Qt::DisplayRole).toString();
}
#endif
#endif
delete m_oldLocalShortcut;
m_oldLocalShortcut = 0;
delete m_oldGlobalShortcut;
m_oldGlobalShortcut = 0;
#if 0
delete m_oldShapeGesture;
m_oldShapeGesture = 0;
delete m_oldRockerGesture;
m_oldRockerGesture = 0;
#endif
}
diff --git a/krita/ui/dialogs/kis_about_application.cpp b/krita/ui/dialogs/kis_about_application.cpp
index 481517216c5..7fa03523c49 100644
--- a/krita/ui/dialogs/kis_about_application.cpp
+++ b/krita/ui/dialogs/kis_about_application.cpp
@@ -1,153 +1,153 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_about_application.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QTabWidget>
#include <QLabel>
#include <QTextEdit>
#include <QString>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <klocale.h>
#include "../data/splash/splash_screen.xpm"
#include "kis_splash_screen.h"
#include "kis_aboutdata.h"
#include "kis_factory2.h"
KisAboutApplication::KisAboutApplication(const K4AboutData *aboutData, QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18n("About Krita"));
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setMargin(0);
QTabWidget *wdg = new QTabWidget;
vlayout->addWidget(wdg);
KisSplashScreen *splash = new KisSplashScreen(aboutData->version(), QPixmap(splash_screen_xpm), true);
splash->setWindowFlags(Qt::Widget);
splash->setFixedSize(splash->sizeHint());
wdg->addTab(splash, i18n("About"));
setMinimumSize(wdg->sizeHint());
QTextEdit *lblAuthors = new QTextEdit();
lblAuthors->setReadOnly(true);
QString authors = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Created By</h1></p>"
"<p>");
foreach(const K4AboutPerson &author, aboutData->authors()) {
authors.append(author.name());
if (!author.task().isEmpty()) {
authors.append(" (<i>" + author.task() + "</i>)");
}
authors.append(", ");
}
authors.chop(2);
authors.append(".</p></body></html>");
lblAuthors->setText(authors);
wdg->addTab(lblAuthors, i18n("Authors"));
QTextEdit *lblKickstarter = new QTextEdit();
lblKickstarter->setReadOnly(true);
QString backers = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Backed By</h1>"
"<p>");
foreach(const K4AboutPerson &backer, aboutData->credits()) {
if (backer.task() == ki18n("Krita 2.9 Kickstarter Backer").toString()) {
backers.append(backer.name() + ", ");
}
}
backers.chop(2);
backers.append(i18n(".</p><p><i>Thanks! You were all <b>awesome</b>!</i></p></body></html>"));
lblKickstarter->setText(backers);
wdg->addTab(lblKickstarter, i18n("Backers"));
QTextEdit *lblCredits = new QTextEdit();
lblCredits->setReadOnly(true);
QString credits = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Thanks To</h1>"
"<p>");
foreach(const K4AboutPerson &credit, aboutData->credits()) {
if (credit.task() != ki18n("Krita 2.9 Kickstarter Backer").toString()) {
credits.append(credit.name());
if (!credit.task().isEmpty()) {
credits.append(" (<i>" + credit.task() + "</i>)");
}
credits.append(", ");
}
}
credits.chop(2);
credits.append(i18n(".</p><p><i>For supporting Krita development with advice, icons, brush sets and more.</i></p></body></html>"));
lblCredits->setText(credits);
wdg->addTab(lblCredits, i18n("Also Thanks To"));
QTextEdit *lblLicense = new QTextEdit();
lblLicense->setReadOnly(true);
QString license = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\"><b>Your Rights</h1>"
"<p>Krita is released under the GNU General Public License (version 2 or any later version).</p>"
"<p>This license grants people a number of freedoms:</p>"
"<ul>"
"<li>You are free to use Krita, for any purpose</li>"
"<li>You are free to distribute Krita</li>"
"<li>You can study how Krita works and change it</li>"
"<li>You can distribute changed versions of Krita</li>"
"</ul>"
"<p>The Krita Foundation and its projects on krita.org are <b>committed</b> to preserving Krita as free software.</p>"
"<h1 align=\"center\">Your artwork</h1>"
"<p>What you create with Krita is your sole property. All your artwork is free for you to use as you like.</p>"
"<p>That means that Krita can be used commercially, for any purpose. There are no restrictions whatsoever.</p>"
"<p>Krita’s GNU GPL license guarantees you this freedom. Nobody is ever permitted to take it away, in contrast "
"to trial or educational versions of commercial software that will forbid your work in commercial situations.</p>"
"<br/><hr/><pre>");
license.append(aboutData->licenses().first().text());
license.append("</pre></body></html>");
lblLicense->setText(license);
wdg->addTab(lblLicense, i18n("License"));
QPushButton *bnClose = new QPushButton(i18n("Close"));
connect(bnClose, SIGNAL(clicked()), SLOT(close()));
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setMargin(0);
hlayout->addStretch(10);
hlayout->addWidget(bnClose);
vlayout->addLayout(hlayout);
}
diff --git a/krita/ui/dialogs/kis_dlg_layer_style.cpp b/krita/ui/dialogs/kis_dlg_layer_style.cpp
index 3d9cdc27499..c16b98de8ad 100644
--- a/krita/ui/dialogs/kis_dlg_layer_style.cpp
+++ b/krita/ui/dialogs/kis_dlg_layer_style.cpp
@@ -1,1433 +1,1433 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_dlg_layer_style.h"
#include <QWidget>
#include <QStackedWidget>
#include <QTreeWidget>
#include <QListWidget>
#include <QListWidgetItem>
#include <QComboBox>
#include <QDial>
#include <QCheckBox>
#include <QSpinBox>
#include <QUuid>
#include <QInputDialog>
#include <KoColorPopupButton.h>
#include <KoResourceServerProvider.h>
#include "kis_config.h"
#include "kis_cmb_contour.h"
#include "kis_cmb_gradient.h"
#include "kis_resource_server_provider.h"
#include "kis_psd_layer_style_resource.h"
#include "kis_psd_layer_style.h"
#include "kis_signals_blocker.h"
#include "kis_signal_compressor.h"
#include "kis_canvas_resource_provider.h"
#include <KoFileDialog.h>
KoAbstractGradient* fetchGradientLazy(KoAbstractGradient *gradient,
KisCanvasResourceProvider *resourceProvider)
{
if (!gradient) {
gradient = resourceProvider->currentGradient();
}
return gradient;
}
KisDlgLayerStyle::KisDlgLayerStyle(KisPSDLayerStyleSP layerStyle, KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: KDialog(parent)
, m_layerStyle(layerStyle)
, m_initialLayerStyle(layerStyle->clone())
, m_isSwitchingPredefinedStyle(false)
, m_sanityLayerStyleDirty(false)
{
setCaption(i18n("Layer Styles"));
setButtons(Ok | Cancel);
setDefaultButton(Ok);
m_configChangedCompressor =
new KisSignalCompressor(1000, KisSignalCompressor::POSTPONE, this);
connect(m_configChangedCompressor, SIGNAL(timeout()), SIGNAL(configChanged()));
QWidget *page = new QWidget(this);
wdgLayerStyles.setupUi(page);
setMainWidget(page);
connect(wdgLayerStyles.lstStyleSelector, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(notifyGuiConfigChanged()));
m_stylesSelector = new StylesSelector(this);
connect(m_stylesSelector, SIGNAL(styleSelected(KisPSDLayerStyleSP)), SLOT(notifyPredefinedStyleSelected(KisPSDLayerStyleSP)));
wdgLayerStyles.stylesStack->addWidget(m_stylesSelector);
m_blendingOptions = new BlendingOptions(this);
wdgLayerStyles.stylesStack->addWidget(m_blendingOptions);
m_dropShadow = new DropShadow(DropShadow::DropShadowMode, this);
wdgLayerStyles.stylesStack->addWidget(m_dropShadow);
connect(m_dropShadow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_innerShadow = new DropShadow(DropShadow::InnerShadowMode, this);
wdgLayerStyles.stylesStack->addWidget(m_innerShadow);
connect(m_innerShadow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_outerGlow = new InnerGlow(InnerGlow::OuterGlowMode, resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_outerGlow);
connect(m_outerGlow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_innerGlow = new InnerGlow(InnerGlow::InnerGlowMode, resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_innerGlow);
connect(m_innerGlow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_contour = new Contour(this);
m_texture = new Texture(this);
m_bevelAndEmboss = new BevelAndEmboss(m_contour, m_texture, this);
wdgLayerStyles.stylesStack->addWidget(m_bevelAndEmboss);
wdgLayerStyles.stylesStack->addWidget(m_contour);
wdgLayerStyles.stylesStack->addWidget(m_texture);
connect(m_bevelAndEmboss, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_satin = new Satin(this);
wdgLayerStyles.stylesStack->addWidget(m_satin);
connect(m_satin, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_colorOverlay = new ColorOverlay(this);
wdgLayerStyles.stylesStack->addWidget(m_colorOverlay);
connect(m_colorOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_gradientOverlay = new GradientOverlay(resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_gradientOverlay);
connect(m_gradientOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_patternOverlay = new PatternOverlay(this);
wdgLayerStyles.stylesStack->addWidget(m_patternOverlay);
connect(m_patternOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_stroke = new Stroke(resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_stroke);
connect(m_stroke, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
KisConfig cfg;
wdgLayerStyles.stylesStack->setCurrentIndex(cfg.readEntry("KisDlgLayerStyle::current", 1));
wdgLayerStyles.lstStyleSelector->setCurrentRow(cfg.readEntry("KisDlgLayerStyle::current", 1));
connect(wdgLayerStyles.lstStyleSelector,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
notifyPredefinedStyleSelected(layerStyle);
connect(m_dropShadow, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(m_innerShadow, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(m_bevelAndEmboss, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(wdgLayerStyles.btnNewStyle, SIGNAL(clicked()), SLOT(slotNewStyle()));
connect(wdgLayerStyles.btnLoadStyle, SIGNAL(clicked()), SLOT(slotLoadStyle()));
connect(wdgLayerStyles.btnSaveStyle, SIGNAL(clicked()), SLOT(slotSaveStyle()));
connect(wdgLayerStyles.chkMasterFxSwitch, SIGNAL(toggled(bool)), SLOT(slotMasterFxSwitchChanged(bool)));
connect(this, SIGNAL(accepted()), SLOT(slotNotifyOnAccept()));
connect(this, SIGNAL(rejected()), SLOT(slotNotifyOnReject()));
}
KisDlgLayerStyle::~KisDlgLayerStyle()
{
}
void KisDlgLayerStyle::slotMasterFxSwitchChanged(bool value)
{
wdgLayerStyles.lstStyleSelector->setEnabled(value);
wdgLayerStyles.stylesStack->setEnabled(value);
wdgLayerStyles.btnNewStyle->setEnabled(value);
wdgLayerStyles.btnLoadStyle->setEnabled(value);
wdgLayerStyles.btnSaveStyle->setEnabled(value);
notifyGuiConfigChanged();
}
void KisDlgLayerStyle::notifyGuiConfigChanged()
{
if (m_isSwitchingPredefinedStyle) return;
m_configChangedCompressor->start();
m_layerStyle->setUuid(QUuid::createUuid());
m_sanityLayerStyleDirty = true;
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
}
void KisDlgLayerStyle::notifyPredefinedStyleSelected(KisPSDLayerStyleSP style)
{
m_isSwitchingPredefinedStyle = true;
setStyle(style);
m_isSwitchingPredefinedStyle = false;
m_configChangedCompressor->start();
}
void KisDlgLayerStyle::slotNotifyOnAccept()
{
if (m_configChangedCompressor->isActive()) {
m_configChangedCompressor->stop();
emit configChanged();
}
}
void KisDlgLayerStyle::slotNotifyOnReject()
{
notifyPredefinedStyleSelected(m_initialLayerStyle);
m_configChangedCompressor->stop();
emit configChanged();
}
bool checkCustomNameAvailable(const QString &name)
{
const QString customName = "CustomStyles.asl";
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
KoResource *resource = server->resourceByName(customName);
if (!resource) return true;
KisPSDLayerStyleCollectionResource *collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
foreach(KisPSDLayerStyleSP style, collection->layerStyles()) {
if (style->name() == name) {
return false;
}
}
return true;
}
QString selectAvailableStyleName(const QString &name)
{
QString finalName = name;
if (checkCustomNameAvailable(finalName)) {
return finalName;
}
int i = 0;
do {
finalName = QString("%1%2").arg(name).arg(i++);
} while (!checkCustomNameAvailable(finalName));
return finalName;
}
void KisDlgLayerStyle::slotNewStyle()
{
QString styleName =
QInputDialog::getText(this,
i18nc("@title:window", "Enter new style name"),
i18nc("@label:textbox", "Name:"),
QLineEdit::Normal, i18nc("Default name for a new style", "New Style"));
KisPSDLayerStyleSP style = this->style();
style->setName(selectAvailableStyleName(styleName));
m_stylesSelector->addNewStyle(style->clone());
}
void KisDlgLayerStyle::slotLoadStyle()
{
QString filename; // default value?
KoFileDialog dialog(this, KoFileDialog::OpenFile, "krita/layerstyle");
dialog.setCaption(i18n("Select ASL file"));
//dialog.setDefaultDir(QDir::cleanPath(filename));
dialog.setNameFilter(i18n("Layer style library (*.asl)"));
filename = dialog.url();
m_stylesSelector->loadCollection(filename);
wdgLayerStyles.lstStyleSelector->setCurrentRow(0);
}
void KisDlgLayerStyle::slotSaveStyle()
{
QString filename; // default value?
KoFileDialog dialog(this, KoFileDialog::SaveFile, "krita/layerstyle");
dialog.setCaption(i18n("Select ASL file"));
//dialog.setDefaultDir(QDir::cleanPath(filename));
dialog.setNameFilter(i18n("Layer style configuration (*.asl)"));
filename = dialog.url();
QScopedPointer<KisPSDLayerStyleCollectionResource> collection(
new KisPSDLayerStyleCollectionResource(filename));
KisPSDLayerStyleSP newStyle = style()->clone();
newStyle->setName(QFileInfo(filename).baseName());
KisPSDLayerStyleCollectionResource::StylesVector vector = collection->layerStyles();
vector << newStyle;
collection->setLayerStyles(vector);
collection->save();
}
void KisDlgLayerStyle::changePage(QListWidgetItem *current, QListWidgetItem *previous)
{
if (!current) {
current = previous;
}
wdgLayerStyles.stylesStack->setCurrentIndex(wdgLayerStyles.lstStyleSelector->row(current));
}
void KisDlgLayerStyle::setStyle(KisPSDLayerStyleSP style)
{
*m_layerStyle = *style;
m_sanityLayerStyleDirty = false;
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
QListWidgetItem *item;
item = wdgLayerStyles.lstStyleSelector->item(2);
item->setCheckState(m_layerStyle->dropShadow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(3);
item->setCheckState(m_layerStyle->innerShadow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(4);
item->setCheckState(m_layerStyle->outerGlow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(5);
item->setCheckState(m_layerStyle->innerGlow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(6);
item->setCheckState(m_layerStyle->bevelAndEmboss()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(7);
item->setCheckState(m_layerStyle->bevelAndEmboss()->contourEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(8);
item->setCheckState(m_layerStyle->bevelAndEmboss()->textureEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(9);
item->setCheckState(m_layerStyle->satin()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(10);
item->setCheckState(m_layerStyle->colorOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(11);
item->setCheckState(m_layerStyle->gradientOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(12);
item->setCheckState(m_layerStyle->patternOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(13);
item->setCheckState(m_layerStyle->stroke()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
m_dropShadow->setShadow(m_layerStyle->dropShadow());
m_innerShadow->setShadow(m_layerStyle->innerShadow());
m_outerGlow->setConfig(m_layerStyle->outerGlow());
m_innerGlow->setConfig(m_layerStyle->innerGlow());
m_bevelAndEmboss->setBevelAndEmboss(m_layerStyle->bevelAndEmboss());
m_satin->setSatin(m_layerStyle->satin());
m_colorOverlay->setColorOverlay(m_layerStyle->colorOverlay());
m_gradientOverlay->setGradientOverlay(m_layerStyle->gradientOverlay());
m_patternOverlay->setPatternOverlay(m_layerStyle->patternOverlay());
m_stroke->setStroke(m_layerStyle->stroke());
wdgLayerStyles.chkMasterFxSwitch->setChecked(m_layerStyle->isEnabled());
slotMasterFxSwitchChanged(m_layerStyle->isEnabled());
}
KisPSDLayerStyleSP KisDlgLayerStyle::style() const
{
m_layerStyle->setEnabled(wdgLayerStyles.chkMasterFxSwitch->isChecked());
m_layerStyle->dropShadow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(2)->checkState() == Qt::Checked);
m_layerStyle->innerShadow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(3)->checkState() == Qt::Checked);
m_layerStyle->outerGlow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(4)->checkState() == Qt::Checked);
m_layerStyle->innerGlow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(5)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(6)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setContourEnabled(wdgLayerStyles.lstStyleSelector->item(7)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setTextureEnabled(wdgLayerStyles.lstStyleSelector->item(8)->checkState() == Qt::Checked);
m_layerStyle->satin()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(9)->checkState() == Qt::Checked);
m_layerStyle->colorOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(10)->checkState() == Qt::Checked);
m_layerStyle->gradientOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(11)->checkState() == Qt::Checked);
m_layerStyle->patternOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(12)->checkState() == Qt::Checked);
m_layerStyle->stroke()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(13)->checkState() == Qt::Checked);
m_dropShadow->fetchShadow(m_layerStyle->dropShadow());
m_innerShadow->fetchShadow(m_layerStyle->innerShadow());
m_outerGlow->fetchConfig(m_layerStyle->outerGlow());
m_innerGlow->fetchConfig(m_layerStyle->innerGlow());
m_bevelAndEmboss->fetchBevelAndEmboss(m_layerStyle->bevelAndEmboss());
m_satin->fetchSatin(m_layerStyle->satin());
m_colorOverlay->fetchColorOverlay(m_layerStyle->colorOverlay());
m_gradientOverlay->fetchGradientOverlay(m_layerStyle->gradientOverlay());
m_patternOverlay->fetchPatternOverlay(m_layerStyle->patternOverlay());
m_stroke->fetchStroke(m_layerStyle->stroke());
m_sanityLayerStyleDirty = false;
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
return m_layerStyle;
}
void KisDlgLayerStyle::syncGlobalAngle(int angle)
{
KisPSDLayerStyleSP style = this->style();
if (style->dropShadow()->useGlobalLight()) {
style->dropShadow()->setAngle(angle);
}
if (style->innerShadow()->useGlobalLight()) {
style->innerShadow()->setAngle(angle);
}
if (style->bevelAndEmboss()->useGlobalLight()) {
style->bevelAndEmboss()->setAngle(angle);
}
setStyle(style);
}
/********************************************************************/
/***** Styles Selector **********************************************/
/********************************************************************/
class StyleItem : public QListWidgetItem {
public:
StyleItem(KisPSDLayerStyleSP style)
: QListWidgetItem(style->name())
, m_style(style)
{
}
public:
KisPSDLayerStyleSP m_style;
};
StylesSelector::StylesSelector(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.cmbStyleCollections, SIGNAL(activated(QString)), this, SLOT(loadStyles(QString)));
connect(ui.listStyles, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(selectStyle(QListWidgetItem*,QListWidgetItem*)));
refillCollections();
if (ui.cmbStyleCollections->count()) {
ui.cmbStyleCollections->setCurrentIndex(0);
loadStyles(ui.cmbStyleCollections->currentText());
}
}
void StylesSelector::refillCollections()
{
QString previousCollection = ui.cmbStyleCollections->currentText();
ui.cmbStyleCollections->clear();
foreach(KoResource *res, KisResourceServerProvider::instance()->layerStyleCollectionServer()->resources()) {
ui.cmbStyleCollections->addItem(res->name());
}
if (!previousCollection.isEmpty()) {
KisSignalsBlocker blocker(this);
int index = ui.cmbStyleCollections->findText(previousCollection);
ui.cmbStyleCollections->setCurrentIndex(index);
}
}
void StylesSelector::notifyExternalStyleChanged(const QString &name, const QUuid &uuid)
{
int currentIndex = -1;
for (int i = 0; i < ui.listStyles->count(); i++ ) {
StyleItem *item = dynamic_cast<StyleItem*>(ui.listStyles->item(i));
QString itemName = item->m_style->name();
if (itemName == name) {
bool isDirty = item->m_style->uuid() != uuid;
if (isDirty) {
itemName += "*";
}
currentIndex = i;
}
item->setText(itemName);
}
ui.listStyles->setCurrentRow(currentIndex);
}
void StylesSelector::loadStyles(const QString &name)
{
ui.listStyles->clear();
KoResource *res = KisResourceServerProvider::instance()->layerStyleCollectionServer()->resourceByName(name);
KisPSDLayerStyleCollectionResource *collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(res);
if (collection) {
foreach(KisPSDLayerStyleSP style, collection->layerStyles()) {
// XXX: also use the preview image, when we have one
ui.listStyles->addItem(new StyleItem(style));
}
}
}
void StylesSelector::selectStyle(QListWidgetItem *current, QListWidgetItem* /*previous*/)
{
StyleItem *item = dynamic_cast<StyleItem*>(current);
if (item) {
emit styleSelected(item->m_style);
}
}
void StylesSelector::loadCollection(const QString &fileName)
{
if (!QFileInfo(fileName).exists()) {
- qWarning() << "Loaded style collection doesn't exist!";
+ warnKrita << "Loaded style collection doesn't exist!";
return;
}
KisPSDLayerStyleCollectionResource *collection =
new KisPSDLayerStyleCollectionResource(fileName);
collection->load();
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
collection->setFilename(server->saveLocation() + QDir::separator() + collection->name());
server->addResource(collection);
refillCollections();
int index = ui.cmbStyleCollections->findText(collection->name());
ui.cmbStyleCollections->setCurrentIndex(index);
loadStyles(collection->name());
}
void StylesSelector::addNewStyle(KisPSDLayerStyleSP style)
{
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
// NOTE: not translatable, since it is a key!
const QString customName = "CustomStyles.asl";
const QString saveLocation = server->saveLocation();
const QString fullFilename = saveLocation + customName;
KoResource *resource = server->resourceByName(customName);
KisPSDLayerStyleCollectionResource *collection = 0;
if (!resource) {
collection = new KisPSDLayerStyleCollectionResource("");
collection->setName(customName);
collection->setFilename(fullFilename);
KisPSDLayerStyleCollectionResource::StylesVector vector;
vector << style;
collection->setLayerStyles(vector);
server->addResource(collection);
} else {
collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
KisPSDLayerStyleCollectionResource::StylesVector vector;
vector = collection->layerStyles();
vector << style;
collection->setLayerStyles(vector);
collection->save();
}
refillCollections();
// select in gui
int index = ui.cmbStyleCollections->findText(customName);
KIS_ASSERT_RECOVER_RETURN(index >= 0);
ui.cmbStyleCollections->setCurrentIndex(index);
loadStyles(customName);
notifyExternalStyleChanged(style->name(), style->uuid());
}
/********************************************************************/
/***** Bevel and Emboss *********************************************/
/********************************************************************/
BevelAndEmboss::BevelAndEmboss(Contour *contour, Texture *texture, QWidget *parent)
: QWidget(parent)
, m_contour(contour)
, m_texture(texture)
{
ui.setupUi(this);
// Structure
ui.intDepth->setRange(0, 100);
ui.intDepth->setSuffix(" %");
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(" px");
ui.intSoften->setRange(0, 18);
ui.intSoften->setSuffix(" px");
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbTechnique, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intDepth, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbDirection, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSoften, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// Shading
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intOpacity2->setRange(0, 100);
ui.intOpacity2->setSuffix(" %");
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled()));
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intAltitude, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbHighlightMode, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnHighlightColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbShadowMode, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnShadowColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.intOpacity2, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));;
// Contour
m_contour->ui.intRange->setRange(0, 100);
m_contour->ui.intRange->setSuffix(" %");
connect(m_contour->ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(m_contour->ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(m_contour->ui.intRange, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// Texture
m_texture->ui.intScale->setRange(0, 100);
m_texture->ui.intScale->setSuffix(" %");
m_texture->ui.intDepth->setRange(-1000, 1000);
m_texture->ui.intDepth->setSuffix(" %");
connect(m_texture->ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(m_texture->ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(m_texture->ui.intDepth, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(m_texture->ui.chkInvert, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(m_texture->ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
}
void BevelAndEmboss::setBevelAndEmboss(const psd_layer_effects_bevel_emboss *bevelAndEmboss)
{
ui.cmbStyle->setCurrentIndex((int)bevelAndEmboss->style());
ui.cmbTechnique->setCurrentIndex((int)bevelAndEmboss->technique());
ui.intDepth->setValue(bevelAndEmboss->depth());
ui.cmbDirection->setCurrentIndex((int)bevelAndEmboss->direction());
ui.intSize->setValue(bevelAndEmboss->size());
ui.intSoften->setValue(bevelAndEmboss->soften());
ui.dialAngle->setValue(bevelAndEmboss->angle());
ui.intAngle->setValue(bevelAndEmboss->angle());
ui.chkUseGlobalLight->setChecked(bevelAndEmboss->useGlobalLight());
ui.intAltitude->setValue(bevelAndEmboss->altitude());
// FIXME: curve editing
// ui.cmbContour;
ui.chkAntiAliased->setChecked(bevelAndEmboss->glossAntiAliased());
ui.cmbHighlightMode->selectCompositeOp(KoID(bevelAndEmboss->highlightBlendMode()));
ui.bnHighlightColor->setColor(bevelAndEmboss->highlightColor());
ui.intOpacity->setValue(bevelAndEmboss->highlightOpacity());
ui.cmbShadowMode->selectCompositeOp(KoID(bevelAndEmboss->shadowBlendMode()));
ui.bnShadowColor->setColor(bevelAndEmboss->shadowColor());
ui.intOpacity2->setValue(bevelAndEmboss->shadowOpacity());
// FIXME: curve editing
// m_contour->ui.cmbContour;
m_contour->ui.chkAntiAliased->setChecked(bevelAndEmboss->antiAliased());
m_contour->ui.intRange->setValue(bevelAndEmboss->contourRange());
m_texture->ui.patternChooser->setCurrentPattern(bevelAndEmboss->texturePattern());
m_texture->ui.intScale->setValue(bevelAndEmboss->textureScale());
m_texture->ui.intDepth->setValue(bevelAndEmboss->textureDepth());
m_texture->ui.chkInvert->setChecked(bevelAndEmboss->textureInvert());
m_texture->ui.chkLinkWithLayer->setChecked(bevelAndEmboss->textureAlignWithLayer());
}
void BevelAndEmboss::fetchBevelAndEmboss(psd_layer_effects_bevel_emboss *bevelAndEmboss) const
{
bevelAndEmboss->setStyle((psd_bevel_style)ui.cmbStyle->currentIndex());
bevelAndEmboss->setTechnique((psd_technique_type)ui.cmbTechnique->currentIndex());
bevelAndEmboss->setDepth(ui.intDepth->value());
bevelAndEmboss->setDirection((psd_direction)ui.cmbDirection->currentIndex());
bevelAndEmboss->setSize(ui.intSize->value());
bevelAndEmboss->setSoften(ui.intSoften->value());
bevelAndEmboss->setAngle(ui.dialAngle->value());
bevelAndEmboss->setUseGlobalLight(ui.chkUseGlobalLight->isChecked());
bevelAndEmboss->setAltitude(ui.intAltitude->value());
bevelAndEmboss->setGlossAntiAliased(ui.chkAntiAliased->isChecked());
bevelAndEmboss->setHighlightBlendMode(ui.cmbHighlightMode->selectedCompositeOp().id());
bevelAndEmboss->setHighlightColor(ui.bnHighlightColor->color());
bevelAndEmboss->setHighlightOpacity(ui.intOpacity->value());
bevelAndEmboss->setShadowBlendMode(ui.cmbShadowMode->selectedCompositeOp().id());
bevelAndEmboss->setShadowColor(ui.bnShadowColor->color());
bevelAndEmboss->setShadowOpacity(ui.intOpacity2->value());
// FIXME: curve editing
bevelAndEmboss->setAntiAliased(m_contour->ui.chkAntiAliased->isChecked());
bevelAndEmboss->setContourRange(m_contour->ui.intRange->value());
bevelAndEmboss->setTexturePattern(static_cast<KoPattern*>(m_texture->ui.patternChooser->currentResource()));
bevelAndEmboss->setTextureScale(m_texture->ui.intScale->value());
bevelAndEmboss->setTextureDepth(m_texture->ui.intDepth->value());
bevelAndEmboss->setTextureInvert(m_texture->ui.chkInvert->isChecked());
bevelAndEmboss->setTextureAlignWithLayer(m_texture->ui.chkLinkWithLayer->isChecked());
}
void BevelAndEmboss::slotDialAngleChanged(int value)
{
KisSignalsBlocker b(ui.intAngle);
ui.intAngle->setValue(value);
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(value);
}
}
void BevelAndEmboss::slotIntAngleChanged(int value)
{
KisSignalsBlocker b(ui.dialAngle);
ui.dialAngle->setValue(value);
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(value);
}
}
void BevelAndEmboss::slotGlobalLightToggled()
{
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(ui.intAngle->value());
}
}
/********************************************************************/
/***** Texture *********************************************/
/********************************************************************/
Texture::Texture(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
/********************************************************************/
/***** Contour *********************************************/
/********************************************************************/
Contour::Contour(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
/********************************************************************/
/***** Blending Options *********************************************/
/********************************************************************/
BlendingOptions::BlendingOptions(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
// FIXME: Blend options are not implemented yet
ui.grpBlendingOptions->setTitle(QString("%1 (%2)").arg(ui.grpBlendingOptions->title()).arg(i18n("Not Implemented Yet")));
ui.grpBlendingOptions->setEnabled(false);
}
/********************************************************************/
/***** Color Overlay *********************************************/
/********************************************************************/
ColorOverlay::ColorOverlay(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
}
void ColorOverlay::setColorOverlay(const psd_layer_effects_color_overlay *colorOverlay)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(colorOverlay->blendMode()));
ui.intOpacity->setValue(colorOverlay->opacity());
ui.bnColor->setColor(colorOverlay->color());
}
void ColorOverlay::fetchColorOverlay(psd_layer_effects_color_overlay *colorOverlay) const
{
colorOverlay->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
colorOverlay->setOpacity(ui.intOpacity->value());
colorOverlay->setColor(ui.bnColor->color());
}
/********************************************************************/
/***** Drop Shadow **************************************************/
/********************************************************************/
DropShadow::DropShadow(Mode mode, QWidget *parent)
: QWidget(parent),
m_mode(mode)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intDistance->setRange(0, 30000);
ui.intDistance->setSuffix(" px");
ui.intSpread->setRange(0, 100);
ui.intSpread->setSuffix(" %");
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(" px");
ui.intNoise->setRange(0, 100);
ui.intNoise->setSuffix(" %");
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled()));
// connect everything to configChanged() signal
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intDistance, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSpread, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intNoise, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.chkLayerKnocksOutDropShadow, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
if (m_mode == InnerShadowMode) {
ui.chkLayerKnocksOutDropShadow->setVisible(false);
ui.grpMain->setTitle(i18n("Inner Shadow"));
ui.lblSpread->setText(i18n("Choke"));
}
}
void DropShadow::slotDialAngleChanged(int value)
{
KisSignalsBlocker b(ui.intAngle);
ui.intAngle->setValue(value);
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(value);
}
}
void DropShadow::slotIntAngleChanged(int value)
{
KisSignalsBlocker b(ui.dialAngle);
ui.dialAngle->setValue(value);
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(value);
}
}
void DropShadow::slotGlobalLightToggled()
{
if (ui.chkUseGlobalLight->isChecked()) {
emit globalAngleChanged(ui.intAngle->value());
}
}
void DropShadow::setShadow(const psd_layer_effects_shadow_common *shadow)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(shadow->blendMode()));
ui.intOpacity->setValue(shadow->opacity());
ui.bnColor->setColor(shadow->color());
ui.dialAngle->setValue(shadow->angle());
ui.intAngle->setValue(shadow->angle());
ui.chkUseGlobalLight->setChecked(shadow->useGlobalLight());
ui.intDistance->setValue(shadow->distance());
ui.intSpread->setValue(shadow->spread());
ui.intSize->setValue(shadow->size());
// FIXME: curve editing
// ui.cmbContour;
ui.chkAntiAliased->setChecked(shadow->antiAliased());
ui.intNoise->setValue(shadow->noise());
if (m_mode == DropShadowMode) {
const psd_layer_effects_drop_shadow *realDropShadow = dynamic_cast<const psd_layer_effects_drop_shadow*>(shadow);
KIS_ASSERT_RECOVER_NOOP(realDropShadow);
ui.chkLayerKnocksOutDropShadow->setChecked(shadow->knocksOut());
}
}
void DropShadow::fetchShadow(psd_layer_effects_shadow_common *shadow) const
{
shadow->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
shadow->setOpacity(ui.intOpacity->value());
shadow->setColor(ui.bnColor->color());
shadow->setAngle(ui.dialAngle->value());
shadow->setUseGlobalLight(ui.chkUseGlobalLight->isChecked());
shadow->setDistance(ui.intDistance->value());
shadow->setSpread(ui.intSpread->value());
shadow->setSize(ui.intSize->value());
// FIXME: curve editing
// ui.cmbContour;
shadow->setAntiAliased(ui.chkAntiAliased->isChecked());
shadow->setNoise(ui.intNoise->value());
if (m_mode == DropShadowMode) {
psd_layer_effects_drop_shadow *realDropShadow = dynamic_cast<psd_layer_effects_drop_shadow*>(shadow);
KIS_ASSERT_RECOVER_NOOP(realDropShadow);
realDropShadow->setKnocksOut(ui.chkLayerKnocksOutDropShadow->isChecked());
}
}
class GradientPointerConverter
{
public:
static KoAbstractGradientSP resourceToStyle(KoAbstractGradient *gradient) {
return gradient ? KoAbstractGradientSP(gradient->clone()) : KoAbstractGradientSP();
}
static KoAbstractGradient* styleToResource(KoAbstractGradientSP gradient) {
if (!gradient) return 0;
KoResourceServer<KoAbstractGradient> *server = KoResourceServerProvider::instance()->gradientServer();
KoAbstractGradient *resource = server->resourceByMD5(gradient->md5());
if (!resource) {
KoAbstractGradient *clone = gradient->clone();
clone->setName(findAvailableName(gradient->name()));
server->addResource(clone, false);
resource = clone;
}
return resource;
}
private:
static QString findAvailableName(const QString &name) {
KoResourceServer<KoAbstractGradient> *server = KoResourceServerProvider::instance()->gradientServer();
QString newName = name;
int i = 0;
while (server->resourceByName(newName)) {
newName = QString("%1%2").arg(name).arg(i++);
}
return newName;
}
};
/********************************************************************/
/***** Gradient Overlay *********************************************/
/********************************************************************/
GradientOverlay::GradientOverlay(KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(" %");
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.chkReverse, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAlignWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
}
void GradientOverlay::setGradientOverlay(const psd_layer_effects_gradient_overlay *config)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(config->blendMode()));
ui.intOpacity->setValue(config->opacity());
KoAbstractGradient *gradient = fetchGradientLazy(
GradientPointerConverter::styleToResource(config->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.chkReverse->setChecked(config->antiAliased());
ui.cmbStyle->setCurrentIndex((int)config->style());
ui.chkAlignWithLayer->setCheckable(config->alignWithLayer());
ui.dialAngle->setValue(config->angle());
ui.intAngle->setValue(config->angle());
ui.intScale->setValue(config->scale());
}
void GradientOverlay::fetchGradientOverlay(psd_layer_effects_gradient_overlay *config) const
{
config->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
config->setOpacity(ui.intOpacity->value());
config->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
config->setReverse(ui.chkReverse->isChecked());
config->setStyle((psd_gradient_style)ui.cmbStyle->currentIndex());
config->setAlignWithLayer(ui.chkAlignWithLayer->isChecked());
config->setAngle(ui.dialAngle->value());
config->setScale(ui.intScale->value());
}
void GradientOverlay::slotDialAngleChanged(int value)
{
KisSignalsBlocker b(ui.intAngle);
ui.intAngle->setValue(value);
}
void GradientOverlay::slotIntAngleChanged(int value)
{
KisSignalsBlocker b(ui.dialAngle);
ui.dialAngle->setValue(value);
}
/********************************************************************/
/***** Innner Glow *********************************************/
/********************************************************************/
InnerGlow::InnerGlow(Mode mode, KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_mode(mode),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intNoise->setRange(0, 100);
ui.intNoise->setSuffix(" %");
ui.intChoke->setRange(0, 100);
ui.intChoke->setSuffix(" %");
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(" px");
ui.intRange->setRange(0, 100);
ui.intRange->setSuffix(" %");
ui.intJitter->setRange(0, 100);
ui.intJitter->setSuffix(" %");
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intNoise, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.radioColor, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.radioGradient, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.cmbTechnique, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbSource, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intChoke, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intRange, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intJitter, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
if (m_mode == OuterGlowMode) {
ui.cmbSource->hide();
ui.lblSource->hide();
ui.lblChoke->setText(i18nc("layer styles parameter", "Spread"));
}
}
void InnerGlow::setConfig(const psd_layer_effects_glow_common *config)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(config->blendMode()));
ui.intOpacity->setValue(config->opacity());
ui.intNoise->setValue(config->noise());
ui.radioColor->setChecked(config->fillType() == psd_fill_solid_color);
ui.bnColor->setColor(config->color());
ui.radioGradient->setChecked(config->fillType() == psd_fill_gradient);
KoAbstractGradient *gradient = fetchGradientLazy(
GradientPointerConverter::styleToResource(config->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.cmbTechnique->setCurrentIndex((int)config->technique());
ui.intChoke->setValue(config->spread());
ui.intSize->setValue(config->size());
if (m_mode == InnerGlowMode) {
const psd_layer_effects_inner_glow *iglow =
dynamic_cast<const psd_layer_effects_inner_glow *>(config);
KIS_ASSERT_RECOVER_RETURN(iglow);
ui.cmbSource->setCurrentIndex(iglow->source() == psd_glow_edge);
}
// FIXME: Curve editing
//ui.cmbContour;
ui.chkAntiAliased->setChecked(config->antiAliased());
ui.intRange->setValue(config->range());
ui.intJitter->setValue(config->jitter());
}
void InnerGlow::fetchConfig(psd_layer_effects_glow_common *config) const
{
config->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
config->setOpacity(ui.intOpacity->value());
config->setNoise(ui.intNoise->value());
if (ui.radioColor->isChecked()) {
config->setFillType(psd_fill_solid_color);
}
else {
config->setFillType(psd_fill_gradient);
}
config->setColor(ui.bnColor->color());
config->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
config->setTechnique((psd_technique_type)ui.cmbTechnique->currentIndex());
config->setSpread(ui.intChoke->value());
config->setSize(ui.intSize->value());
if (m_mode == InnerGlowMode) {
psd_layer_effects_inner_glow *iglow =
dynamic_cast<psd_layer_effects_inner_glow *>(config);
KIS_ASSERT_RECOVER_RETURN(iglow);
iglow->setSource((psd_glow_source)ui.cmbSource->currentIndex());
}
// FIXME: Curve editing
//ui.cmbContour;
config->setAntiAliased(ui.chkAntiAliased->isChecked());
config->setRange(ui.intRange->value());
config->setJitter(ui.intJitter->value());
}
/********************************************************************/
/***** Pattern Overlay *********************************************/
/********************************************************************/
PatternOverlay::PatternOverlay(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(" %");
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
}
void PatternOverlay::setPatternOverlay(const psd_layer_effects_pattern_overlay *pattern)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(pattern->blendMode()));
ui.intOpacity->setValue(pattern->opacity());
ui.patternChooser->setCurrentPattern(pattern->pattern());
ui.chkLinkWithLayer->setChecked(pattern->alignWithLayer());
ui.intScale->setValue(pattern->scale());
}
void PatternOverlay::fetchPatternOverlay(psd_layer_effects_pattern_overlay *pattern) const
{
pattern->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
pattern->setOpacity(ui.intOpacity->value());
pattern->setPattern(static_cast<KoPattern*>(ui.patternChooser->currentResource()));
pattern->setAlignWithLayer(ui.chkLinkWithLayer->isChecked());
pattern->setScale(ui.intScale->value());
}
/********************************************************************/
/***** Satin *********************************************/
/********************************************************************/
Satin::Satin(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intDistance->setRange(0, 250);
ui.intDistance->setSuffix(" px");
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(" px");
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intDistance, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.chkInvert, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
}
void Satin::slotDialAngleChanged(int value)
{
KisSignalsBlocker b(ui.intAngle);
ui.intAngle->setValue(value);
}
void Satin::slotIntAngleChanged(int value)
{
KisSignalsBlocker b(ui.dialAngle);
ui.dialAngle->setValue(value);
}
void Satin::setSatin(const psd_layer_effects_satin *satin)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(satin->blendMode()));
ui.bnColor->setColor(satin->color());
ui.intOpacity->setValue(satin->opacity());
ui.dialAngle->setValue(satin->angle());
ui.intAngle->setValue(satin->angle());
ui.intDistance->setValue(satin->distance());
ui.intSize->setValue(satin->size());
// FIXME: Curve editing
//ui.cmbContour;
ui.chkAntiAliased->setChecked(satin->antiAliased());
ui.chkInvert->setChecked(satin->invert());
}
void Satin::fetchSatin(psd_layer_effects_satin *satin) const
{
satin->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
satin->setOpacity(ui.intOpacity->value());
satin->setColor(ui.bnColor->color());
satin->setAngle(ui.dialAngle->value());
satin->setDistance(ui.intDistance->value());
satin->setSize(ui.intSize->value());
// FIXME: curve editing
// ui.cmbContour;
satin->setAntiAliased(ui.chkAntiAliased->isChecked());
satin->setInvert(ui.chkInvert->isChecked());
}
/********************************************************************/
/***** Stroke *********************************************/
/********************************************************************/
Stroke::Stroke(KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(" px");
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(" %");
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(" %");
ui.intScale_2->setRange(0, 100);
ui.intScale_2->setSuffix(" %");
connect(ui.cmbFillType, SIGNAL(currentIndexChanged(int)), ui.fillStack, SLOT(setCurrentIndex(int)));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbPosition, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbFillType, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(QColor)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.chkReverse, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAlignWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intScale_2, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// cold initialization
ui.fillStack->setCurrentIndex(ui.cmbFillType->currentIndex());
}
void Stroke::slotDialAngleChanged(int value)
{
KisSignalsBlocker b(ui.intAngle);
ui.intAngle->setValue(value);
}
void Stroke::slotIntAngleChanged(int value)
{
KisSignalsBlocker b(ui.dialAngle);
ui.dialAngle->setValue(value);
}
void Stroke::setStroke(const psd_layer_effects_stroke *stroke)
{
ui.intSize->setValue(stroke->size());
ui.cmbPosition->setCurrentIndex((int)stroke->position());
ui.cmbCompositeOp->selectCompositeOp(KoID(stroke->blendMode()));
ui.intOpacity->setValue(stroke->opacity());
ui.cmbFillType->setCurrentIndex((int)stroke->fillType());
ui.bnColor->setColor(stroke->color());
KoAbstractGradient *gradient =
fetchGradientLazy(GradientPointerConverter::styleToResource(stroke->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.chkReverse->setChecked(stroke->antiAliased());
ui.cmbStyle->setCurrentIndex((int)stroke->style());
ui.chkAlignWithLayer->setCheckable(stroke->alignWithLayer());
ui.dialAngle->setValue(stroke->angle());
ui.intAngle->setValue(stroke->angle());
ui.intScale->setValue(stroke->scale());
ui.patternChooser->setCurrentPattern(stroke->pattern());
ui.chkLinkWithLayer->setChecked(stroke->alignWithLayer());
ui.intScale_2->setValue(stroke->scale());
}
void Stroke::fetchStroke(psd_layer_effects_stroke *stroke) const
{
stroke->setSize(ui.intSize->value());
stroke->setPosition((psd_stroke_position)ui.cmbPosition->currentIndex());
stroke->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
stroke->setOpacity(ui.intOpacity->value());
stroke->setFillType((psd_fill_type)ui.cmbFillType->currentIndex());
stroke->setColor(ui.bnColor->color());
stroke->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
stroke->setReverse(ui.chkReverse->isChecked());
stroke->setStyle((psd_gradient_style)ui.cmbStyle->currentIndex());
stroke->setAlignWithLayer(ui.chkAlignWithLayer->isChecked());
stroke->setAngle(ui.dialAngle->value());
stroke->setScale(ui.intScale->value());
stroke->setPattern(static_cast<KoPattern*>(ui.patternChooser->currentResource()));
stroke->setAlignWithLayer(ui.chkLinkWithLayer->isChecked());
stroke->setScale(ui.intScale->value());
}
diff --git a/krita/ui/dialogs/kis_dlg_preferences.cc b/krita/ui/dialogs/kis_dlg_preferences.cc
index 1d3e5e9b11a..0fdcfee8f31 100644
--- a/krita/ui/dialogs/kis_dlg_preferences.cc
+++ b/krita/ui/dialogs/kis_dlg_preferences.cc
@@ -1,1125 +1,1125 @@
/*
* preferencesdlg.cc - part of KImageShop
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2003-2011 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_dlg_preferences.h"
#include <opengl/kis_opengl.h>
#include <QBitmap>
#include <QCheckBox>
#include <QCursor>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QSlider>
#include <QToolButton>
#include <QThread>
#include <QDesktopServices>
#include <QGridLayout>
#include <QRadioButton>
#include <QGroupBox>
#include <QMdiArea>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QFileDialog>
#include <boost/bind.hpp>
#include <KisDocument.h>
#include <KoColorProfile.h>
#include <KisApplication.h>
#include <KoFileDialog.h>
#include <KisPart.h>
#include <KoColorSpaceEngine.h>
#include <kis_icon_utils.h>
#include <KoConfig.h>
#include "KoID.h"
#include <KoConfigAuthorPage.h>
#include <klocale.h>
#include <kvbox.h>
#include <kundo2stack.h>
#include <kstandarddirs.h>
#include "widgets/squeezedcombobox.h"
#include "kis_clipboard.h"
#include "widgets/kis_cmb_idlist.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "kis_cursor.h"
#include "kis_config.h"
#include "kis_canvas_resource_provider.h"
#include "kis_preference_set_registry.h"
#include "kis_factory2.h"
#include "kis_color_manager.h"
#include "slider_and_spin_box_sync.h"
// for the performance update
#include <kis_cubic_curve.h>
#include "input/config/kis_input_configuration_page.h"
GeneralTab::GeneralTab(QWidget *_parent, const char *_name)
: WdgGeneralSettings(_parent, _name)
{
KisConfig cfg;
m_cmbCursorShape->addItem(i18n("No Cursor"));
m_cmbCursorShape->addItem(i18n("Tool Icon"));
m_cmbCursorShape->addItem(i18n("Arrow"));
m_cmbCursorShape->addItem(i18n("Small Circle"));
m_cmbCursorShape->addItem(i18n("Crosshair"));
m_cmbCursorShape->addItem(i18n("Triangle Righthanded"));
m_cmbCursorShape->addItem(i18n("Triangle Lefthanded"));
m_cmbOutlineShape->addItem(i18n("No Outline"));
m_cmbOutlineShape->addItem(i18n("Circle Outline"));
m_cmbOutlineShape->addItem(i18n("Preview Outline"));
m_cmbOutlineShape->addItem(i18n("Tilt Outline"));
m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle());
m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle());
chkShowRootLayer->setChecked(cfg.showRootLayer());
int autosaveInterval = cfg.autoSaveInterval();
//convert to minutes
m_autosaveSpinBox->setValue(autosaveInterval / 60);
m_autosaveCheckBox->setChecked(autosaveInterval > 0);
m_undoStackSize->setValue(cfg.undoStackLimit());
m_backupFileCheckBox->setChecked(cfg.backupFile());
m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting());
m_hideSplashScreen->setChecked(cfg.hideSplashScreen());
m_cmbMDIType->setCurrentIndex(cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView));
m_chkRubberBand->setChecked(cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
m_favoritePresetsSpinBox->setValue(cfg.favoritePresets());
m_mdiColor->setColor(cfg.getMDIBackgroundColor());
m_backgroundimage->setText(cfg.getMDIBackgroundImage());
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages());
m_chkCompressKra->setChecked(cfg.compressKra());
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker());
connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage()));
connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage()));
}
void GeneralTab::setDefault()
{
KisConfig cfg;
m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true));
m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true));
chkShowRootLayer->setChecked(cfg.showRootLayer(true));
m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0);
//convert to minutes
m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60);
m_undoStackSize->setValue(cfg.undoStackLimit(true));
m_backupFileCheckBox->setChecked(cfg.backupFile(true));
m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true));
m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true));
m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView);
m_chkRubberBand->setChecked(cfg.useOpenGL(true));
m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true));
m_mdiColor->setColor(cfg.getMDIBackgroundColor(true));
m_backgroundimage->setText(cfg.getMDIBackgroundImage(true));
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true));
m_chkCompressKra->setChecked(cfg.compressKra(true));
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true));
}
CursorStyle GeneralTab::cursorStyle()
{
return (CursorStyle)m_cmbCursorShape->currentIndex();
}
OutlineStyle GeneralTab::outlineStyle()
{
return (OutlineStyle)m_cmbOutlineShape->currentIndex();
}
bool GeneralTab::showRootLayer()
{
return chkShowRootLayer->isChecked();
}
int GeneralTab::autoSaveInterval()
{
//convert to seconds
return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value()*60 : 0;
}
int GeneralTab::undoStackSize()
{
return m_undoStackSize->value();
}
bool GeneralTab::showOutlineWhilePainting()
{
return m_showOutlinePainting->isChecked();
}
bool GeneralTab::hideSplashScreen()
{
return m_hideSplashScreen->isChecked();
}
int GeneralTab::mdiMode()
{
return m_cmbMDIType->currentIndex();
}
int GeneralTab::favoritePresets()
{
return m_favoritePresetsSpinBox->value();
}
bool GeneralTab::showCanvasMessages()
{
return m_chkCanvasMessages->isChecked();
}
bool GeneralTab::compressKra()
{
return m_chkCompressKra->isChecked();
}
bool GeneralTab::toolOptionsInDocker()
{
return m_radioToolOptionsInDocker->isChecked();
}
void GeneralTab::getBackgroundImage()
{
KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages");
dialog.setCaption(i18n("Select a Background Image"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setImageFilters();
QString fn = dialog.url();
// dialog box was canceled or somehow no file was selected
if (fn.isEmpty()) {
return;
}
QImage image(fn);
if (image.isNull()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn));
}
else {
m_backgroundimage->setText(fn);
}
}
void GeneralTab::clearBackgroundImage()
{
// clearing the background image text will implicitly make the background color be used
m_backgroundimage->setText("");
}
ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name)
: QWidget(parent)
{
setObjectName(name);
// XXX: Make sure only profiles that fit the specified color model
// are shown in the profile combos
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgColorSettings(this);
l->addWidget(m_page, 0, 0);
KisConfig cfg;
m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile());
connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool)));
// XXX: no color management integration on Windows or OSX yet
#ifndef HAVE_X11
m_page->chkUseSystemMonitorProfile->setVisible(false);
#endif
m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys());
m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace());
m_page->cmbPrintingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys());
m_page->cmbPrintingColorSpace->setCurrent(cfg.printerColorSpace());
m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open"));
m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") );
connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile()));
refillPrintProfiles(KoID(cfg.printerColorSpace(), ""));
//hide printing settings
m_page->groupBox2->hide();
QGridLayout *monitorProfileGrid = new QGridLayout(m_page->monitorprofileholder);
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1));
monitorProfileGrid->addWidget(lbl, i, 0);
m_monitorProfileLabels << lbl;
SqueezedComboBox *cmb = new SqueezedComboBox();
monitorProfileGrid->addWidget(cmb, i, 1);
m_monitorProfileWidgets << cmb;
}
refillMonitorProfiles(KoID("RGBA", ""));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
if (m_page->cmbPrintProfile->contains(cfg.printerProfile()))
m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile()));
m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation());
m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization());
m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB);
m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR);
m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK);
QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour());
Q_ASSERT(button);
if (button) {
button->setChecked(true);
}
m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent());
toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile());
connect(m_page->cmbPrintingColorSpace, SIGNAL(activated(const KoID &)),
this, SLOT(refillPrintProfiles(const KoID &)));
}
void ColorSettingsTab::installProfile()
{
QStringList mime;
mime << "ICM Profile (*.icm)" << "ICC Profile (*.icc)";
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC");
dialog.setCaption(i18n("Install Color Profiles"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
dialog.setNameFilters(mime);
QStringList profileNames = dialog.urls();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
QString saveLocation = KGlobal::dirs()->saveLocation("icc_profiles");
foreach (const QString &profileName, profileNames) {
KUrl file(profileName);
if (!QFile::copy(profileName, saveLocation + file.fileName())) {
- kWarning() << "Could not install profile!";
+ dbgKrita << "Could not install profile!";
return;
}
iccEngine->addProfile(saveLocation + file.fileName());
}
KisConfig cfg;
refillMonitorProfiles(KoID("RGBA", ""));
refillPrintProfiles(KoID(cfg.printerColorSpace(), ""));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
if (m_page->cmbPrintProfile->contains(cfg.printerProfile()))
m_page->cmbPrintProfile->setCurrentIndex(m_page->cmbPrintProfile->findText(cfg.printerProfile()));
}
void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile)
{
if (useSystemProfile) {
KisConfig cfg;
QStringList devices = KisColorManager::instance()->devices();
if (devices.size() == QApplication::desktop()->screenCount()) {
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->clear();
QString monitorForScreen = cfg.monitorForScreen(i, devices[i]);
foreach(const QString &device, devices) {
m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1));
m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device);
if (devices[i] == monitorForScreen) {
m_monitorProfileWidgets[i]->setCurrentIndex(i);
}
}
}
}
}
else {
KisConfig cfg;
refillMonitorProfiles(KoID("RGBA", ""));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
}
}
void ColorSettingsTab::setDefault()
{
m_page->cmbWorkingColorSpace->setCurrent("RGBA");
m_page->cmbPrintingColorSpace->setCurrent("CMYK");
refillPrintProfiles(KoID("CMYK", ""));
refillMonitorProfiles(KoID("RGBA", ""));
KisConfig cfg;
m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true));
m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true));
m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true));
m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true));
QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true));
Q_ASSERT(button);
if (button) {
button->setChecked(true);
}
}
void ColorSettingsTab::refillMonitorProfiles(const KoID & s)
{
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id());
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->clear();
}
if (!csf)
return;
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
foreach (const KoColorProfile *profile, profileList) {
-// //qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile();
+// //dbgKrita << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile();
if (profile->isSuitableForDisplay()) {
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->addSqueezedItem(profile->name());
}
}
}
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1));
m_monitorProfileWidgets[i]->setCurrent(csf->defaultProfile());
}
}
void ColorSettingsTab::refillPrintProfiles(const KoID & s)
{
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s.id());
m_page->cmbPrintProfile->clear();
if (!csf)
return;
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
foreach(const KoColorProfile *profile, profileList) {
if (profile->isSuitableForPrinting())
m_page->cmbPrintProfile->addSqueezedItem(profile->name());
}
m_page->cmbPrintProfile->setCurrent(csf->defaultProfile());
}
//---------------------------------------------------------------------------------------------------
void TabletSettingsTab::setDefault()
{
KisCubicCurve curve;
curve.fromString(DEFAULT_CURVE_STRING);
m_page->pressureCurve->setCurve(curve);
}
TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent)
{
setObjectName(name);
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgTabletSettings(this);
l->addWidget(m_page, 0, 0);
KisConfig cfg;
KisCubicCurve curve;
curve.fromString( cfg.pressureTabletCurve() );
m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
m_page->pressureCurve->setCurve(curve);
}
//---------------------------------------------------------------------------------------------------
#include "kis_image_config.h"
#include "kis_acyclic_signal_connector.h"
int getTotalRAM() {
KisImageConfig cfg;
return cfg.totalRAM();
}
int PerformanceTab::realTilesRAM()
{
return intMemoryLimit->value() - intPoolLimit->value();
}
PerformanceTab::PerformanceTab(QWidget *parent, const char *name)
: WdgPerformanceSettings(parent, name)
{
KisImageConfig cfg;
const int totalRAM = cfg.totalRAM();
lblTotalMemory->setText(i18n("%1 MiB", totalRAM));
sliderMemoryLimit->setSuffix(" %");
sliderMemoryLimit->setRange(1, 100, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderPoolLimit->setSuffix(" %");
sliderPoolLimit->setRange(0, 20, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderUndoLimit->setSuffix(" %");
sliderUndoLimit->setRange(0, 50, 2);
sliderMemoryLimit->setSingleStep(0.01);
intMemoryLimit->setMinimumWidth(80);
intPoolLimit->setMinimumWidth(80);
intUndoLimit->setMinimumWidth(80);
SliderAndSpinBoxSync *sync1 =
new SliderAndSpinBoxSync(sliderMemoryLimit,
intMemoryLimit,
getTotalRAM);
sync1->slotParentValueChanged();
m_syncs << sync1;
SliderAndSpinBoxSync *sync2 =
new SliderAndSpinBoxSync(sliderPoolLimit,
intPoolLimit,
boost::bind(&QSpinBox::value,
intMemoryLimit));
connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged()));
sync2->slotParentValueChanged();
m_syncs << sync2;
SliderAndSpinBoxSync *sync3 =
new SliderAndSpinBoxSync(sliderUndoLimit,
intUndoLimit,
boost::bind(&PerformanceTab::realTilesRAM,
this));
connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged()));
sync3->slotParentValueChanged();
m_syncs << sync3;
sliderSwapSize->setSuffix(i18n(" GiB"));
sliderSwapSize->setRange(1, 64);
intSwapSize->setRange(1, 64);
KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this);
swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)),
intSwapSize, SLOT(setValue(int)));
swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)),
sliderSwapSize, SLOT(setValue(int)));
lblSwapFileLocation->setText(cfg.swapDir());
connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir()));
load(false);
}
PerformanceTab::~PerformanceTab()
{
qDeleteAll(m_syncs);
}
void PerformanceTab::load(bool requestDefault)
{
KisImageConfig cfg;
sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault));
sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault));
sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault));
chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault));
chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault));
sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024);
lblSwapFileLocation->setText(cfg.swapDir(requestDefault));
}
void PerformanceTab::save()
{
KisImageConfig cfg;
cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value());
cfg.setMemorySoftLimitPercent(sliderUndoLimit->value());
cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value());
cfg.setEnablePerfLog(chkPerformanceLogging->isChecked());
cfg.setEnableProgressReporting(chkProgressReporting->isChecked());
cfg.setMaxSwapSize(sliderSwapSize->value() * 1024);
cfg.setSwapDir(lblSwapFileLocation->text());
}
void PerformanceTab::selectSwapDir()
{
KisImageConfig cfg;
QString swapDir = cfg.swapDir();
swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir);
lblSwapFileLocation->setText(swapDir);
}
//---------------------------------------------------------------------------------------------------
#include "KoColor.h"
#include "KoColorPopupAction.h"
DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name)
: WdgDisplaySettings(parent, name)
{
KisConfig cfg;
#ifdef HAVE_OPENGL
if (!KisOpenGL::hasOpenGL()) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->setEnabled(false);
chkDisableDoubleBuffering->setEnabled(false);
chkDisableVsync->setEnabled(false);
cmbFilterMode->setEnabled(false);
} else {
grpOpenGL->setEnabled(true);
grpOpenGL->setChecked(cfg.useOpenGL());
chkUseTextureBuffer->setEnabled(cfg.useOpenGL());
chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer());
chkDisableDoubleBuffering->setVisible(cfg.showAdvancedOpenGLSettings());
chkDisableDoubleBuffering->setEnabled(cfg.useOpenGL());
chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering());
chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings());
chkDisableVsync->setEnabled(cfg.useOpenGL());
chkDisableVsync->setChecked(cfg.disableVSync());
cmbFilterMode->setEnabled(cfg.useOpenGL());
cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode());
// Don't show the high quality filtering mode if it's not available
if (!KisOpenGL::supportsGLSL13()) {
cmbFilterMode->removeItem(3);
}
}
if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") {
grpOpenGL->setVisible(false);
grpOpenGL->setMaximumHeight(0);
}
#else
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
#endif
KoColor c;
c.fromQColor(cfg.selectionOverlayMaskColor());
m_selectionOverlayColorAction = new KoColorPopupAction(this);
m_selectionOverlayColorAction->setCurrentColor(c);
m_selectionOverlayColorAction->setIcon(KisIconUtils::loadIcon("format-stroke-color"));
m_selectionOverlayColorAction->setToolTip(i18n("Change the background color of the image"));
btnSelectionOverlayColor->setDefaultAction(m_selectionOverlayColorAction);
intCheckSize->setValue(cfg.checkSize());
chkMoving->setChecked(cfg.scrollCheckers());
colorChecks1->setColor(cfg.checkersColor1());
colorChecks2->setColor(cfg.checkersColor2());
canvasBorder->setColor(cfg.canvasBorderColor());
hideScrollbars->setChecked(cfg.hideScrollbars());
chkCurveAntialiasing->setChecked(cfg.antialiasCurves());
chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline());
chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor());
chkHidePopups->setChecked(cfg.hidePopups());
connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool)));
}
void DisplaySettingsTab::setDefault()
{
KisConfig cfg;
#ifdef HAVE_OPENGL
if (!KisOpenGL::hasOpenGL()) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->setEnabled(false);
chkDisableDoubleBuffering->setEnabled(false);
chkDisableVsync->setEnabled(false);
cmbFilterMode->setEnabled(false);
}
else {
grpOpenGL->setEnabled(true);
grpOpenGL->setChecked(cfg.useOpenGL(true));
chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true));
chkUseTextureBuffer->setEnabled(true);
chkDisableDoubleBuffering->setEnabled(true);
chkDisableDoubleBuffering->setChecked(cfg.disableDoubleBuffering(true));
chkDisableVsync->setEnabled(true);
chkDisableVsync->setChecked(cfg.disableVSync(true));
cmbFilterMode->setEnabled(true);
cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true));
}
#else
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
#endif
chkMoving->setChecked(cfg.scrollCheckers(true));
intCheckSize->setValue(cfg.checkSize(true));
colorChecks1->setColor(cfg.checkersColor1(true));
colorChecks2->setColor(cfg.checkersColor2(true));
canvasBorder->setColor(cfg.canvasBorderColor(true));
hideScrollbars->setChecked(cfg.hideScrollbars(true));
chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true));
chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true));
chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true));
chkHidePopups->setChecked(cfg.hidePopups(true));
}
void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked)
{
#ifdef HAVE_OPENGL
chkUseTextureBuffer->setEnabled(isChecked);
chkDisableDoubleBuffering->setEnabled(isChecked);
chkDisableVsync->setEnabled(isChecked);
cmbFilterMode->setEnabled(isChecked);
#else
Q_UNUSED(isChecked);
#endif
}
//---------------------------------------------------------------------------------------------------
GridSettingsTab::GridSettingsTab(QWidget* parent) : WdgGridSettingsBase(parent)
{
KisConfig cfg;
selectMainStyle->setCurrentIndex(cfg.getGridMainStyle());
selectSubdivisionStyle->setCurrentIndex(cfg.getGridSubdivisionStyle());
colorMain->setColor(cfg.getGridMainColor());
colorSubdivision->setColor(cfg.getGridSubdivisionColor());
intHSpacing->setValue(cfg.getGridHSpacing());
intVSpacing->setValue(cfg.getGridVSpacing());
spacingAspectButton->setKeepAspectRatio(cfg.getGridSpacingAspect());
linkSpacingToggled(cfg.getGridSpacingAspect());
intSubdivision->setValue(cfg.getGridSubdivisions());
intXOffset->setValue(cfg.getGridOffsetX());
intYOffset->setValue(cfg.getGridOffsetY());
offsetAspectButton->setKeepAspectRatio(cfg.getGridOffsetAspect());
linkOffsetToggled(cfg.getGridOffsetAspect());
connect(spacingAspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(linkSpacingToggled(bool)));
connect(offsetAspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(linkOffsetToggled(bool)));
connect(intHSpacing, SIGNAL(valueChanged(int)), this, SLOT(spinBoxHSpacingChanged(int)));
connect(intVSpacing, SIGNAL(valueChanged(int)), this, SLOT(spinBoxVSpacingChanged(int)));
connect(intXOffset, SIGNAL(valueChanged(int)), this, SLOT(spinBoxXOffsetChanged(int)));
connect(intYOffset, SIGNAL(valueChanged(int)), this, SLOT(spinBoxYOffsetChanged(int)));
}
void GridSettingsTab::setDefault()
{
KisConfig cfg;
selectMainStyle->setCurrentIndex(cfg.getGridMainStyle(true));
selectSubdivisionStyle->setCurrentIndex(cfg.getGridSubdivisionStyle(true));
colorMain->setColor(cfg.getGridMainColor(true));
colorSubdivision->setColor(cfg.getGridSubdivisionColor(true));
intHSpacing->setValue(cfg.getGridHSpacing(true));
intVSpacing->setValue(cfg.getGridVSpacing(true));
linkSpacingToggled(cfg.getGridSpacingAspect(true));
intSubdivision->setValue(cfg.getGridSubdivisions(true));
intXOffset->setValue(cfg.getGridOffsetX(true));
intYOffset->setValue(cfg.getGridOffsetY());
linkOffsetToggled(cfg.getGridOffsetAspect(true));
}
void GridSettingsTab::spinBoxHSpacingChanged(int v)
{
if (m_linkSpacing) {
intVSpacing->setValue(v);
}
}
void GridSettingsTab::spinBoxVSpacingChanged(int v)
{
if (m_linkSpacing) {
intHSpacing->setValue(v);
}
}
void GridSettingsTab::linkSpacingToggled(bool b)
{
m_linkSpacing = b;
if (m_linkSpacing) {
intVSpacing->setValue(intHSpacing->value());
}
}
void GridSettingsTab::spinBoxXOffsetChanged(int v)
{
if (m_linkOffset) {
intYOffset->setValue(v);
}
}
void GridSettingsTab::spinBoxYOffsetChanged(int v)
{
if (m_linkOffset) {
intXOffset->setValue(v);
}
}
void GridSettingsTab::linkOffsetToggled(bool b)
{
m_linkOffset = b;
if (m_linkOffset) {
intYOffset->setValue(intXOffset->value());
}
}
//---------------------------------------------------------------------------------------------------
FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent)
{
KisConfig cfg;
chkDockers->setChecked(cfg.hideDockersFullscreen());
chkMenu->setChecked(cfg.hideMenuFullscreen());
chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen());
chkStatusbar->setChecked(cfg.hideStatusbarFullscreen());
chkTitlebar->setChecked(cfg.hideTitlebarFullscreen());
chkToolbar->setChecked(cfg.hideToolbarFullscreen());
}
void FullscreenSettingsTab::setDefault()
{
KisConfig cfg;
chkDockers->setChecked(cfg.hideDockersFullscreen(true));
chkMenu->setChecked(cfg.hideMenuFullscreen(true));
chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true));
chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true));
chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true));
chkToolbar->setChecked(cfg.hideToolbarFullscreen(true));
}
//---------------------------------------------------------------------------------------------------
KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name)
: KPageDialog(parent)
{
Q_UNUSED(name);
setWindowTitle(i18n("Preferences"));
// QT5TODO: help button needs custom wiring up to whatever help should be shown
setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults);
button(QDialogButtonBox::Ok)->setDefault(true);
setFaceType(KPageDialog::List);
// General
KVBox *vbox = new KVBox();
KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General"));
page->setObjectName("general");
page->setHeader(i18n("General"));
page->setIcon(KisIconUtils::loadIcon("configure"));
addPage(page);
m_general = new GeneralTab(vbox);
// Display
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Display"));
page->setObjectName("display");
page->setHeader(i18n("Display"));
page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display"));
addPage(page);
m_displaySettings = new DisplaySettingsTab(vbox);
// Color
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Color Management"));
page->setObjectName("colormanagement");
page->setHeader(i18n("Color"));
page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color"));
addPage(page);
m_colorSettings = new ColorSettingsTab(vbox);
// Performance
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Performance"));
page->setObjectName("performance");
page->setHeader(i18n("Performance"));
page->setIcon(KisIconUtils::loadIcon("applications-system"));
addPage(page);
m_performanceSettings = new PerformanceTab(vbox);
// Grid
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Grid"));
page->setObjectName("grid");
page->setHeader(i18n("Grid"));
page->setIcon(KisIconUtils::loadIcon("view-grid"));
addPage(page);
m_gridSettings = new GridSettingsTab(vbox);
// Tablet
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Tablet settings"));
page->setObjectName("tablet");
page->setHeader(i18n("Tablet"));
page->setIcon(KisIconUtils::loadIcon("document-edit"));
addPage(page);
m_tabletSettings = new TabletSettingsTab(vbox);
// full-screen mode
vbox = new KVBox();
page = new KPageWidgetItem(vbox, i18n("Canvas-only settings"));
page->setObjectName("canvasonly");
page->setHeader(i18n("Canvas-only"));
page->setIcon(KisIconUtils::loadIcon("folder-pictures"));
addPage(page);
m_fullscreenSettings = new FullscreenSettingsTab(vbox);
// Author profiles
m_authorPage = new KoConfigAuthorPage();
page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" ));
page->setObjectName("author");
page->setHeader(i18n("Author"));
page->setIcon(KisIconUtils::loadIcon("im-user"));
// input settings
m_inputConfiguration = new KisInputConfigurationPage();
page = addPage(m_inputConfiguration, i18n("Canvas Input Settings"));
page->setHeader(i18n("Canvas Input"));
page->setObjectName("canvasinput");
page->setIcon(KisIconUtils::loadIcon("applications-system"));
QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults);
connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges()));
connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges()));
connect(restoreDefaultsButton, SIGNAL(clicked(bool)), m_inputConfiguration, SLOT(setDefaults()));
KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance();
foreach (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) {
KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet();
vbox = new KVBox();
page = new KPageWidgetItem(vbox, preferenceSet->name());
page->setHeader(preferenceSet->header());
page->setIcon(preferenceSet->icon());
addPage(page);
preferenceSet->setParent(vbox);
preferenceSet->loadPreferences();
connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection);
connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection);
}
connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault()));
}
KisDlgPreferences::~KisDlgPreferences()
{
}
void KisDlgPreferences::slotDefault()
{
if (currentPage()->objectName() == "default") {
m_general->setDefault();
}
else if (currentPage()->objectName() == "display") {
m_displaySettings->setDefault();
}
else if (currentPage()->objectName() == "colormanagement") {
m_colorSettings->setDefault();
}
else if (currentPage()->objectName() == "performance") {
m_performanceSettings->load(true);
}
else if (currentPage()->objectName() == "grid") {
m_gridSettings->setDefault();
}
else if (currentPage()->objectName() == "tablet") {
m_tabletSettings->setDefault();
}
else if (currentPage()->objectName() == "canvasonly") {
m_fullscreenSettings->setDefault();
}
else if (currentPage()->objectName() == "canvasinput") {
m_inputConfiguration->setDefaults();
}
}
bool KisDlgPreferences::editPreferences()
{
KisDlgPreferences* dialog;
dialog = new KisDlgPreferences();
bool baccept = (dialog->exec() == Accepted);
if (baccept) {
// General settings
KisConfig cfg;
cfg.setNewCursorStyle(dialog->m_general->cursorStyle());
cfg.setNewOutlineStyle(dialog->m_general->outlineStyle());
cfg.setShowRootLayer(dialog->m_general->showRootLayer());
cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting());
cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen());
cfg.writeEntry<int>("mdi_viewmode", dialog->m_general->mdiMode());
cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color());
cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text());
cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval());
cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked());
cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages());
cfg.setCompressKra(dialog->m_general->compressKra());
cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker());
KisPart *part = KisPart::instance();
if (part) {
foreach(QPointer<KisDocument> doc, part->documents()) {
if (doc) {
doc->setAutoSave(dialog->m_general->autoSaveInterval());
doc->setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked());
doc->undoStack()->setUndoLimit(dialog->m_general->undoStackSize());
}
}
}
cfg.setUndoStackLimit(dialog->m_general->undoStackSize());
cfg.setFavoritePresets(dialog->m_general->favoritePresets());
// Color settings
cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) {
int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex();
QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString();
cfg.setMonitorForScreen(i, monitorid);
}
else {
cfg.setMonitorProfile(i,
dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(),
dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
}
}
cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id());
cfg.setPrinterColorSpace(dialog->m_colorSettings->m_page->cmbPrintingColorSpace->currentItem().id());
cfg.setPrinterProfile(dialog->m_colorSettings->m_page->cmbPrintProfile->itemHighlighted());
cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked());
cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked());
cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId());
cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex());
// Tablet settings
cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() );
dialog->m_performanceSettings->save();
#ifdef HAVE_OPENGL
if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked())
cfg.setCanvasState("TRY_OPENGL");
cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked());
cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked());
cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex());
cfg.setDisableDoubleBuffering(dialog->m_displaySettings->chkDisableDoubleBuffering->isChecked());
cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked());
#endif
cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value());
cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked());
cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color());
cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color());
cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color());
cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked());
cfg.setSelectionOverlayMaskColor(dialog->m_displaySettings->m_selectionOverlayColorAction->currentKoColor().toQColor());
cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked());
cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked());
cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked());
cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked());
// Grid settings
cfg.setGridMainStyle(dialog->m_gridSettings->selectMainStyle->currentIndex());
cfg.setGridSubdivisionStyle(dialog->m_gridSettings->selectSubdivisionStyle->currentIndex());
cfg.setGridMainColor(dialog->m_gridSettings->colorMain->color());
cfg.setGridSubdivisionColor(dialog->m_gridSettings->colorSubdivision->color());
cfg.setGridHSpacing(dialog->m_gridSettings->intHSpacing->value());
cfg.setGridVSpacing(dialog->m_gridSettings->intVSpacing->value());
cfg.setGridSpacingAspect(dialog->m_gridSettings->spacingAspectButton->keepAspectRatio());
cfg.setGridSubdivisions(dialog->m_gridSettings->intSubdivision->value());
cfg.setGridOffsetX(dialog->m_gridSettings->intXOffset->value());
cfg.setGridOffsetY(dialog->m_gridSettings->intYOffset->value());
cfg.setGridOffsetAspect(dialog->m_gridSettings->offsetAspectButton->keepAspectRatio());
cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState());
cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState());
cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState());
cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState());
cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState());
cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState());
dialog->m_authorPage->apply();
}
delete dialog;
return baccept;
}
diff --git a/krita/ui/flake/kis_shape_layer.cc b/krita/ui/flake/kis_shape_layer.cc
index 14dca5965d3..e2201496d30 100644
--- a/krita/ui/flake/kis_shape_layer.cc
+++ b/krita/ui/flake/kis_shape_layer.cc
@@ -1,617 +1,617 @@
/*
* Copyright (c) 2006-2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2007 Thomas Zander <zander@kde.org>
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Jan Hambrecht <jaham@gmx.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_shape_layer.h"
#include <QPainter>
#include <QPainterPath>
#include <QRect>
#include <QDomElement>
#include <QDomDocument>
#include <QString>
#include <QList>
#include <QMap>
-#include <QDebug>
+#include <kis_debug.h>
#include <kundo2command.h>
#include <commands_new/kis_node_move_command2.h>
#include <QMimeData>
#include <QTemporaryFile>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KoIcon.h>
#include <KoElementReference.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoDataCenterBase.h>
#include <KisDocument.h>
#include <KoEmbeddedDocumentSaver.h>
#include <KoGenStyles.h>
#include <KoImageCollection.h>
#include <KoUnit.h>
#include <KoOdf.h>
#include <KoOdfReadStore.h>
#include <KoOdfStylesReader.h>
#include <KoOdfWriteStore.h>
#include <KoPageLayout.h>
#include <KoShapeContainer.h>
#include <KoShapeLayer.h>
#include <KoShapeGroup.h>
#include <KoShapeLoadingContext.h>
#include <KoShapeManager.h>
#include <KoShapeRegistry.h>
#include <KoShapeSavingContext.h>
#include <KoStore.h>
#include <KoShapeBasedDocumentBase.h>
#include <KoStoreDevice.h>
#include <KoViewConverter.h>
#include <KoXmlNS.h>
#include <KoXmlReader.h>
#include <KoXmlWriter.h>
#include <KoSelection.h>
#include <KoShapeMoveCommand.h>
#include <KoShapeTransformCommand.h>
#include <KoShapeShadow.h>
#include <KoShapeShadowCommand.h>
#include <kis_types.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include "kis_shape_layer_canvas.h"
#include "kis_image_view_converter.h"
#include <kis_painter.h>
#include "kis_node_visitor.h"
#include "kis_processing_visitor.h"
#include "kis_effect_mask.h"
#include "kis_shape_layer_paste.h"
#include <SimpleShapeContainerModel.h>
class ShapeLayerContainerModel : public SimpleShapeContainerModel
{
public:
ShapeLayerContainerModel(KisShapeLayer *parent)
: q(parent)
{}
void add(KoShape *child) {
SimpleShapeContainerModel::add(child);
q->shapeManager()->addShape(child);
}
void remove(KoShape *child) {
q->shapeManager()->remove(child);
SimpleShapeContainerModel::remove(child);
}
private:
KisShapeLayer *q;
};
struct KisShapeLayer::Private
{
public:
Private()
: converter(0)
, canvas(0)
, controller(0)
, x(0)
, y(0)
{}
KoViewConverter * converter;
KisPaintDeviceSP paintDevice;
KisShapeLayerCanvas * canvas;
KoShapeBasedDocumentBase* controller;
int x;
int y;
};
KisShapeLayer::KisShapeLayer(KoShapeBasedDocumentBase* controller,
KisImageWSP image,
const QString &name,
quint8 opacity)
: KisExternalLayer(image, name, opacity),
KoShapeLayer(new ShapeLayerContainerModel(this)),
m_d(new Private())
{
initShapeLayer(controller);
}
KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs)
: KisExternalLayer(_rhs)
, KoShapeLayer(new ShapeLayerContainerModel(this)) //no _rhs here otherwise both layer have the same KoShapeContainerModel
, m_d(new Private())
{
// Make sure our new layer is visible otherwise the shapes cannot be painted.
setVisible(true);
initShapeLayer(_rhs.m_d->controller);
KoShapeOdfSaveHelper saveHelper(_rhs.shapes());
KoDrag drag;
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QMimeData* mimeData = drag.mimeData();
Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text)));
KisShapeLayerShapePaste paste(this, m_d->controller);
bool success = paste.paste(KoOdf::Text, mimeData);
Q_ASSERT(success);
Q_UNUSED(success); // for release build
}
KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, const KisShapeLayer &_addShapes)
: KisExternalLayer(_rhs)
, KoShapeLayer(new ShapeLayerContainerModel(this)) //no _merge here otherwise both layer have the same KoShapeContainerModel
, m_d(new Private())
{
// Make sure our new layer is visible otherwise the shapes cannot be painted.
setVisible(true);
initShapeLayer(_rhs.m_d->controller);
// copy in _rhs's shapes
{
KoShapeOdfSaveHelper saveHelper(_rhs.shapes());
KoDrag drag;
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QMimeData* mimeData = drag.mimeData();
Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text)));
KisShapeLayerShapePaste paste(this, m_d->controller);
bool success = paste.paste(KoOdf::Text, mimeData);
Q_ASSERT(success);
Q_UNUSED(success); // for release build
}
// copy in _addShapes's shapes
{
KoShapeOdfSaveHelper saveHelper(_addShapes.shapes());
KoDrag drag;
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QMimeData* mimeData = drag.mimeData();
Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text)));
KisShapeLayerShapePaste paste(this, m_d->controller);
bool success = paste.paste(KoOdf::Text, mimeData);
Q_ASSERT(success);
Q_UNUSED(success); // for release build
}
}
KisShapeLayer::~KisShapeLayer()
{
/**
* Small hack alert: we set the image to null to disable
* updates those will be emitted on shape deletion
*/
KisLayer::setImage(0);
foreach(KoShape *shape, shapes()) {
shape->setParent(0);
delete shape;
}
delete m_d->converter;
delete m_d->canvas;
delete m_d;
}
void KisShapeLayer::initShapeLayer(KoShapeBasedDocumentBase* controller)
{
setShapeId(KIS_SHAPE_LAYER_ID);
m_d->converter = new KisImageViewConverter(image());
m_d->paintDevice = new KisPaintDevice(image()->colorSpace());
m_d->canvas = new KisShapeLayerCanvas(this, m_d->converter);
m_d->canvas->setProjection(m_d->paintDevice);
m_d->controller = controller;
m_d->canvas->shapeManager()->selection()->disconnect(this);
connect(m_d->canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged()));
connect(m_d->canvas->shapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*)),
this, SIGNAL(currentLayerChanged(const KoShapeLayer*)));
connect(this, SIGNAL(sigMoveShapes(const QPointF&)), SLOT(slotMoveShapes(const QPointF&)));
}
bool KisShapeLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
}
void KisShapeLayer::setImage(KisImageWSP _image)
{
KisLayer::setImage(_image);
delete m_d->converter;
m_d->converter = new KisImageViewConverter(image());
m_d->paintDevice = new KisPaintDevice(image()->colorSpace());
}
KisLayerSP KisShapeLayer::createMergedLayer(KisLayerSP prevLayer)
{
KisShapeLayer *prevShape = dynamic_cast<KisShapeLayer*>(prevLayer.data());
if (prevShape)
return new KisShapeLayer(*prevShape, *this);
else
return KisExternalLayer::createMergedLayer(prevLayer);
}
void KisShapeLayer::setParent(KoShapeContainer *parent)
{
Q_UNUSED(parent)
KIS_ASSERT_RECOVER_RETURN(0)
}
QIcon KisShapeLayer::icon() const
{
return themedIcon("bookmarks");
}
KisPaintDeviceSP KisShapeLayer::original() const
{
return m_d->paintDevice;
}
KisPaintDeviceSP KisShapeLayer::paintDevice() const
{
return 0;
}
qint32 KisShapeLayer::x() const
{
return m_d->x;
}
qint32 KisShapeLayer::y() const
{
return m_d->y;
}
void KisShapeLayer::setX(qint32 x)
{
qint32 delta = x - this->x();
QPointF diff = QPointF(m_d->converter->viewToDocumentX(delta), 0);
emit sigMoveShapes(diff);
// Save new value to satisfy LSP
m_d->x = x;
}
void KisShapeLayer::setY(qint32 y)
{
qint32 delta = y - this->y();
QPointF diff = QPointF(0, m_d->converter->viewToDocumentY(delta));
emit sigMoveShapes(diff);
// Save new value to satisfy LSP
m_d->y = y;
}
void KisShapeLayer::slotMoveShapes(const QPointF &diff)
{
QList<QPointF> prevPos;
QList<QPointF> newPos;
QList<KoShape*> shapes;
foreach (KoShape* shape, shapeManager()->shapes()) {
if (!dynamic_cast<KoShapeGroup*>(shape)) {
shapes.append(shape);
}
}
foreach (KoShape* shape, shapes) {
QPointF pos = shape->position();
prevPos << pos;
newPos << pos + diff;
}
KoShapeMoveCommand cmd(shapes, prevPos, newPos);
cmd.redo();
}
bool KisShapeLayer::accept(KisNodeVisitor& visitor)
{
return visitor.visit(this);
}
void KisShapeLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
KoShapeManager* KisShapeLayer::shapeManager() const
{
return m_d->canvas->shapeManager();
}
KoViewConverter* KisShapeLayer::converter() const
{
return m_d->converter;
}
bool KisShapeLayer::visible(bool recursive) const
{
return KisExternalLayer::visible(recursive);
}
void KisShapeLayer::setVisible(bool visible, bool isLoading)
{
KisExternalLayer::setVisible(visible, isLoading);
}
bool KisShapeLayer::saveLayer(KoStore * store) const
{
KoOdfWriteStore odfStore(store);
KoXmlWriter* manifestWriter = odfStore.manifestWriter("application/vnd.oasis.opendocument.graphics");
KoEmbeddedDocumentSaver embeddedSaver;
KisDocument::SavingContext documentContext(odfStore, embeddedSaver);
if (!store->open("content.xml"))
return false;
KoStoreDevice storeDev(store);
KoXmlWriter * docWriter = KoOdfWriteStore::createOasisXmlWriter(&storeDev, "office:document-content");
// for office:master-styles
QTemporaryFile masterStyles;
masterStyles.open();
KoXmlWriter masterStylesTmpWriter(&masterStyles, 1);
KoPageLayout page;
page.format = KoPageFormat::defaultFormat();
QRectF rc = boundingRect();
page.width = rc.width();
page.height = rc.height();
if (page.width > page.height) {
page.orientation = KoPageFormat::Landscape;
} else {
page.orientation = KoPageFormat::Portrait;
}
KoGenStyles mainStyles;
KoGenStyle pageLayout = page.saveOdf();
QString layoutName = mainStyles.insert(pageLayout, "PL");
KoGenStyle masterPage(KoGenStyle::MasterPageStyle);
masterPage.addAttribute("style:page-layout-name", layoutName);
mainStyles.insert(masterPage, "Default", KoGenStyles::DontAddNumberToName);
QTemporaryFile contentTmpFile;
contentTmpFile.open();
KoXmlWriter contentTmpWriter(&contentTmpFile, 1);
contentTmpWriter.startElement("office:body");
contentTmpWriter.startElement("office:drawing");
KoShapeSavingContext shapeContext(contentTmpWriter, mainStyles, documentContext.embeddedSaver);
shapeContext.xmlWriter().startElement("draw:page");
shapeContext.xmlWriter().addAttribute("draw:name", "");
KoElementReference elementRef("page", 1);
elementRef.saveOdf(&shapeContext.xmlWriter(), KoElementReference::DrawId);
shapeContext.xmlWriter().addAttribute("draw:master-page-name", "Default");
saveOdf(shapeContext);
shapeContext.xmlWriter().endElement(); // draw:page
contentTmpWriter.endElement(); // office:drawing
contentTmpWriter.endElement(); // office:body
mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, docWriter);
// And now we can copy over the contents from the tempfile to the real one
contentTmpFile.seek(0);
docWriter->addCompleteElement(&contentTmpFile);
docWriter->endElement(); // Root element
docWriter->endDocument();
delete docWriter;
if (!store->close())
return false;
embeddedSaver.saveEmbeddedDocuments(documentContext);
manifestWriter->addManifestEntry("content.xml", "text/xml");
if (! mainStyles.saveOdfStylesDotXml(store, manifestWriter)) {
return false;
}
manifestWriter->addManifestEntry("settings.xml", "text/xml");
if (! shapeContext.saveDataCenter(documentContext.odfStore.store(),
documentContext.odfStore.manifestWriter()))
return false;
// Write out manifest file
if (!odfStore.closeManifestWriter()) {
dbgImage << "closing manifestWriter failed";
return false;
}
return true;
}
bool KisShapeLayer::loadLayer(KoStore* store)
{
KoOdfReadStore odfStore(store);
QString errorMessage;
odfStore.loadAndParse(errorMessage);
if (!errorMessage.isEmpty()) {
warnKrita << errorMessage;
return false;
}
KoXmlElement contents = odfStore.contentDoc().documentElement();
- // qDebug() <<"Start loading OASIS document..." << contents.text();
- // qDebug() <<"Start loading OASIS contents..." << contents.lastChild().localName();
- // qDebug() <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI();
- // qDebug() <<"Start loading OASIS contents..." << contents.lastChild().isElement();
+ // dbgKrita <<"Start loading OASIS document..." << contents.text();
+ // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName();
+ // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI();
+ // dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement();
KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body"));
if (body.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) );
return false;
}
body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing");
if (body.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) );
return false;
}
KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page"));
if (page.isNull()) {
//setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) );
return false;
}
KoXmlElement * master = 0;
if (odfStore.styles().masterPages().contains("Standard"))
master = odfStore.styles().masterPages().value("Standard");
else if (odfStore.styles().masterPages().contains("Default"))
master = odfStore.styles().masterPages().value("Default");
else if (! odfStore.styles().masterPages().empty())
master = odfStore.styles().masterPages().begin().value();
if (master) {
const KoXmlElement *style = odfStore.styles().findStyle(
master->attributeNS(KoXmlNS::style, "page-layout-name", QString()));
KoPageLayout pageLayout;
pageLayout.loadOdf(*style);
setSize(QSizeF(pageLayout.width, pageLayout.height));
}
// We work fine without a master page
KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
context.setManifestFile(QString("tar:/") + odfStore.store()->currentPath() + "META-INF/manifest.xml");
KoShapeLoadingContext shapeContext(context, m_d->controller->resourceManager());
KoXmlElement layerElement;
forEachElement(layerElement, context.stylesReader().layerSet()) {
// FIXME: investigate what is this
// KoShapeLayer * l = new KoShapeLayer();
if (!loadOdf(layerElement, shapeContext)) {
- kWarning() << "Could not load vector layer!";
+ dbgKrita << "Could not load vector layer!";
return false;
}
}
KoXmlElement child;
forEachElement(child, page) {
KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext);
if (shape) {
addShape(shape);
}
}
return true;
}
void KisShapeLayer::resetCache()
{
m_d->paintDevice->clear();
QList<KoShape*> shapes = m_d->canvas->shapeManager()->shapes();
foreach(const KoShape* shape, shapes) {
shape->update();
}
}
KUndo2Command* KisShapeLayer::crop(const QRect & rect) {
QPoint oldPos(x(), y());
QPoint newPos = oldPos - rect.topLeft();
return new KisNodeMoveCommand2(this, oldPos, newPos);
}
KUndo2Command* KisShapeLayer::transform(const QTransform &transform) {
QList<KoShape*> shapes = m_d->canvas->shapeManager()->shapes();
if(shapes.isEmpty()) return 0;
KisImageViewConverter *converter = dynamic_cast<KisImageViewConverter*>(m_d->converter);
QTransform realTransform = converter->documentToView() *
transform * converter->viewToDocument();
QList<QTransform> oldTransformations;
QList<QTransform> newTransformations;
QList<KoShapeShadow*> newShadows;
const qreal transformBaseScale = KoUnit::approxTransformScale(transform);
// this code won't work if there are shapes, that inherit the transformation from the parent container.
// the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that.
foreach(const KoShape* shape, shapes) {
QTransform oldTransform = shape->transformation();
oldTransformations.append(oldTransform);
if (dynamic_cast<const KoShapeGroup*>(shape)) {
newTransformations.append(oldTransform);
} else {
QTransform globalTransform = shape->absoluteTransformation(0);
QTransform localTransform = globalTransform * realTransform * globalTransform.inverted();
newTransformations.append(localTransform*oldTransform);
}
KoShapeShadow *shadow = 0;
if (shape->shadow()) {
shadow = new KoShapeShadow(*shape->shadow());
shadow->setOffset(transformBaseScale * shadow->offset());
shadow->setBlur(transformBaseScale * shadow->blur());
}
newShadows.append(shadow);
}
KUndo2Command *parentCommand = new KUndo2Command();
new KoShapeTransformCommand(shapes,
oldTransformations,
newTransformations,
parentCommand);
new KoShapeShadowCommand(shapes,
newShadows,
parentCommand);
return parentCommand;
}
diff --git a/krita/ui/flake/kis_shape_selection.cpp b/krita/ui/flake/kis_shape_selection.cpp
index fcdeb615145..5983815d891 100644
--- a/krita/ui/flake/kis_shape_selection.cpp
+++ b/krita/ui/flake/kis_shape_selection.cpp
@@ -1,451 +1,451 @@
/*
* Copyright (c) 2010 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (c) 2011 Jan Hambrecht <jaham@gmx.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_shape_selection.h"
#include <QPainter>
#include <QTimer>
#include <kundo2command.h>
#include <QMimeData>
#include <QTemporaryFile>
#include <KoShapeStroke.h>
#include <KoPathShape.h>
#include <KoShapeGroup.h>
#include <KoCompositeOp.h>
#include <KoShapeManager.h>
#include <KisDocument.h>
#include <KoEmbeddedDocumentSaver.h>
#include <KoGenStyles.h>
#include <KoOdfLoadingContext.h>
#include <KoOdfReadStore.h>
#include <KoOdfStylesReader.h>
#include <KoOdfWriteStore.h>
#include <KoXmlNS.h>
#include <KoShapeRegistry.h>
#include <KoShapeLoadingContext.h>
#include <KoXmlWriter.h>
#include <KoStore.h>
#include <KoShapeController.h>
#include <KoShapeSavingContext.h>
#include <KoStoreDevice.h>
#include <KoShapeTransformCommand.h>
#include <KoElementReference.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_image.h>
#include <kis_iterator_ng.h>
#include <kis_selection.h>
#include "kis_shape_selection_model.h"
#include "kis_shape_selection_canvas.h"
#include "kis_shape_layer_paste.h"
#include "kis_take_all_shapes_command.h"
#include "kis_image_view_converter.h"
#include <kis_debug.h>
KisShapeSelection::KisShapeSelection(KisImageWSP image, KisSelectionWSP selection)
: KoShapeLayer(m_model = new KisShapeSelectionModel(image, selection, this))
, m_image(image)
{
Q_ASSERT(m_image);
setShapeId("KisShapeSelection");
setSelectable(false);
m_converter = new KisImageViewConverter(image);
m_canvas = new KisShapeSelectionCanvas();
m_canvas->shapeManager()->addShape(this);
m_model->moveToThread(image->thread());
m_canvas->moveToThread(image->thread());
}
KisShapeSelection::~KisShapeSelection()
{
m_model->setShapeSelection(0);
delete m_canvas;
delete m_converter;
}
KisShapeSelection::KisShapeSelection(const KisShapeSelection& rhs, KisSelection* selection)
: KoShapeLayer(m_model = new KisShapeSelectionModel(rhs.m_image, selection, this))
{
m_image = rhs.m_image;
m_converter = new KisImageViewConverter(m_image);
m_canvas = new KisShapeSelectionCanvas();
m_canvas->shapeManager()->addShape(this);
KoShapeOdfSaveHelper saveHelper(rhs.shapes());
KoDrag drag;
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QMimeData* mimeData = drag.mimeData();
Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text)));
KisShapeLayerShapePaste paste(this, 0);
bool success = paste.paste(KoOdf::Text, mimeData);
Q_ASSERT(success);
if (!success) {
warnUI << "Could not paste vector layer";
}
}
KisSelectionComponent* KisShapeSelection::clone(KisSelection* selection)
{
return new KisShapeSelection(*this, selection);
}
bool KisShapeSelection::saveSelection(KoStore * store) const
{
KoOdfWriteStore odfStore(store);
KoXmlWriter* manifestWriter = odfStore.manifestWriter("application/vnd.oasis.opendocument.graphics");
KoEmbeddedDocumentSaver embeddedSaver;
KisDocument::SavingContext documentContext(odfStore, embeddedSaver);
if (!store->open("content.xml"))
return false;
KoStoreDevice storeDev(store);
KoXmlWriter * docWriter = KoOdfWriteStore::createOasisXmlWriter(&storeDev, "office:document-content");
// for office:master-styles
QTemporaryFile masterStyles;
masterStyles.open();
KoXmlWriter masterStylesTmpWriter(&masterStyles, 1);
KoPageLayout page;
page.format = KoPageFormat::defaultFormat();
QRectF rc = boundingRect();
page.width = rc.width();
page.height = rc.height();
if (page.width > page.height) {
page.orientation = KoPageFormat::Landscape;
} else {
page.orientation = KoPageFormat::Portrait;
}
KoGenStyles mainStyles;
KoGenStyle pageLayout = page.saveOdf();
QString layoutName = mainStyles.insert(pageLayout, "PL");
KoGenStyle masterPage(KoGenStyle::MasterPageStyle);
masterPage.addAttribute("style:page-layout-name", layoutName);
mainStyles.insert(masterPage, "Default", KoGenStyles::DontAddNumberToName);
QTemporaryFile contentTmpFile;
contentTmpFile.open();
KoXmlWriter contentTmpWriter(&contentTmpFile, 1);
contentTmpWriter.startElement("office:body");
contentTmpWriter.startElement("office:drawing");
KoShapeSavingContext shapeContext(contentTmpWriter, mainStyles, documentContext.embeddedSaver);
shapeContext.xmlWriter().startElement("draw:page");
shapeContext.xmlWriter().addAttribute("draw:name", "");
KoElementReference elementRef;
elementRef.saveOdf(&shapeContext.xmlWriter(), KoElementReference::DrawId);
shapeContext.xmlWriter().addAttribute("draw:master-page-name", "Default");
saveOdf(shapeContext);
shapeContext.xmlWriter().endElement(); // draw:page
contentTmpWriter.endElement(); // office:drawing
contentTmpWriter.endElement(); // office:body
mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, docWriter);
// And now we can copy over the contents from the tempfile to the real one
contentTmpFile.seek(0);
docWriter->addCompleteElement(&contentTmpFile);
docWriter->endElement(); // Root element
docWriter->endDocument();
delete docWriter;
if (!store->close())
return false;
manifestWriter->addManifestEntry("content.xml", "text/xml");
if (! mainStyles.saveOdfStylesDotXml(store, manifestWriter)) {
return false;
}
manifestWriter->addManifestEntry("settings.xml", "text/xml");
if (! shapeContext.saveDataCenter(documentContext.odfStore.store(), documentContext.odfStore.manifestWriter()))
return false;
// Write out manifest file
if (!odfStore.closeManifestWriter()) {
dbgImage << "closing manifestWriter failed";
return false;
}
return true;
}
bool KisShapeSelection::loadSelection(KoStore* store)
{
KoOdfReadStore odfStore(store);
QString errorMessage;
odfStore.loadAndParse(errorMessage);
if (!errorMessage.isEmpty()) {
- qDebug() << errorMessage;
+ dbgKrita << errorMessage;
return false;
}
KoXmlElement contents = odfStore.contentDoc().documentElement();
-// qDebug() <<"Start loading OASIS document..." << contents.text();
-// qDebug() <<"Start loading OASIS contents..." << contents.lastChild().localName();
-// qDebug() <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI();
-// qDebug() <<"Start loading OASIS contents..." << contents.lastChild().isElement();
+// dbgKrita <<"Start loading OASIS document..." << contents.text();
+// dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().localName();
+// dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().namespaceURI();
+// dbgKrita <<"Start loading OASIS contents..." << contents.lastChild().isElement();
KoXmlElement body(KoXml::namedItemNS(contents, KoXmlNS::office, "body"));
if (body.isNull()) {
- qDebug() << "No office:body found!";
+ dbgKrita << "No office:body found!";
//setErrorMessage( i18n( "Invalid OASIS document. No office:body tag found." ) );
return false;
}
body = KoXml::namedItemNS(body, KoXmlNS::office, "drawing");
if (body.isNull()) {
- qDebug() << "No office:drawing found!";
+ dbgKrita << "No office:drawing found!";
//setErrorMessage( i18n( "Invalid OASIS document. No office:drawing tag found." ) );
return false;
}
KoXmlElement page(KoXml::namedItemNS(body, KoXmlNS::draw, "page"));
if (page.isNull()) {
- qDebug() << "No office:drawing found!";
+ dbgKrita << "No office:drawing found!";
//setErrorMessage( i18n( "Invalid OASIS document. No draw:page tag found." ) );
return false;
}
KoXmlElement * master = 0;
if (odfStore.styles().masterPages().contains("Standard"))
master = odfStore.styles().masterPages().value("Standard");
else if (odfStore.styles().masterPages().contains("Default"))
master = odfStore.styles().masterPages().value("Default");
else if (! odfStore.styles().masterPages().empty())
master = odfStore.styles().masterPages().begin().value();
if (master) {
const KoXmlElement *style = odfStore.styles().findStyle(
master->attributeNS(KoXmlNS::style, "page-layout-name", QString()));
KoPageLayout pageLayout;
pageLayout.loadOdf(*style);
setSize(QSizeF(pageLayout.width, pageLayout.height));
} else {
- kWarning() << "No master page found!";
+ dbgKrita << "No master page found!";
return false;
}
KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
KoShapeLoadingContext shapeContext(context, 0);
KoXmlElement layerElement;
forEachElement(layerElement, context.stylesReader().layerSet()) {
if (!loadOdf(layerElement, shapeContext)) {
- kWarning() << "Could not load vector layer!";
+ dbgKrita << "Could not load vector layer!";
return false;
}
}
KoXmlElement child;
forEachElement(child, page) {
KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, shapeContext);
if (shape) {
addShape(shape);
}
}
return true;
}
void KisShapeSelection::setUpdatesEnabled(bool enabled)
{
m_model->setUpdatesEnabled(enabled);
}
bool KisShapeSelection::updatesEnabled() const
{
return m_model->updatesEnabled();
}
KUndo2Command* KisShapeSelection::resetToEmpty()
{
return new KisTakeAllShapesCommand(this, true);
}
bool KisShapeSelection::isEmpty() const
{
return !m_model->count();
}
QPainterPath KisShapeSelection::outlineCache() const
{
return m_outline;
}
bool KisShapeSelection::outlineCacheValid() const
{
return true;
}
void KisShapeSelection::recalculateOutlineCache()
{
QList<KoShape*> shapesList = shapes();
QPainterPath outline;
foreach(KoShape * shape, shapesList) {
QTransform shapeMatrix = shape->absoluteTransformation(0);
outline = outline.united(shapeMatrix.map(shape->outline()));
}
QTransform resolutionMatrix;
resolutionMatrix.scale(m_image->xRes(), m_image->yRes());
m_outline = resolutionMatrix.map(outline);
}
void KisShapeSelection::paintComponent(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &)
{
Q_UNUSED(painter);
Q_UNUSED(converter);
}
void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection)
{
Q_ASSERT(projection);
Q_ASSERT(m_image);
QRectF boundingRect = outlineCache().boundingRect();
renderSelection(projection, boundingRect.toAlignedRect());
}
void KisShapeSelection::renderToProjection(KisPaintDeviceSP projection, const QRect& r)
{
Q_ASSERT(projection);
renderSelection(projection, r);
}
void KisShapeSelection::renderSelection(KisPaintDeviceSP projection, const QRect& r)
{
Q_ASSERT(projection);
Q_ASSERT(m_image);
const qint32 MASK_IMAGE_WIDTH = 256;
const qint32 MASK_IMAGE_HEIGHT = 256;
QImage polygonMaskImage(MASK_IMAGE_WIDTH, MASK_IMAGE_HEIGHT, QImage::Format_ARGB32);
QPainter maskPainter(&polygonMaskImage);
maskPainter.setRenderHint(QPainter::Antialiasing, true);
// Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
for (qint32 x = r.x(); x < r.x() + r.width(); x += MASK_IMAGE_WIDTH) {
for (qint32 y = r.y(); y < r.y() + r.height(); y += MASK_IMAGE_HEIGHT) {
maskPainter.fillRect(polygonMaskImage.rect(), Qt::black);
maskPainter.translate(-x, -y);
maskPainter.fillPath(outlineCache(), Qt::white);
maskPainter.translate(x, y);
qint32 rectWidth = qMin(r.x() + r.width() - x, MASK_IMAGE_WIDTH);
qint32 rectHeight = qMin(r.y() + r.height() - y, MASK_IMAGE_HEIGHT);
KisSequentialIterator it(projection, QRect(x, y, rectWidth, rectHeight));
do {
(*it.rawData()) = qRed(polygonMaskImage.pixel(it.x() - x, it.y() - y));
} while (it.nextPixel());
}
}
}
KoShapeManager* KisShapeSelection::shapeManager() const
{
return m_canvas->shapeManager();
}
KisShapeSelectionFactory::KisShapeSelectionFactory()
: KoShapeFactoryBase("KisShapeSelection", "selection shape container")
{
setHidden(true);
}
void KisShapeSelection::moveX(qint32 x)
{
foreach (KoShape* shape, shapeManager()->shapes()) {
if (shape != this) {
QPointF pos = shape->position();
shape->setPosition(QPointF(pos.x() + x/m_image->xRes(), pos.y()));
}
}
}
void KisShapeSelection::moveY(qint32 y)
{
foreach (KoShape* shape, shapeManager()->shapes()) {
if (shape != this) {
QPointF pos = shape->position();
shape->setPosition(QPointF(pos.x(), pos.y() + y/m_image->yRes()));
}
}
}
// TODO same code as in vector layer, refactor!
KUndo2Command* KisShapeSelection::transform(const QTransform &transform) {
QList<KoShape*> shapes = m_canvas->shapeManager()->shapes();
if(shapes.isEmpty()) return 0;
QTransform realTransform = m_converter->documentToView() *
transform * m_converter->viewToDocument();
QList<QTransform> oldTransformations;
QList<QTransform> newTransformations;
// this code won't work if there are shapes, that inherit the transformation from the parent container.
// the chart and tree shapes are examples for that, but they aren't used in krita and there are no other shapes like that.
foreach(const KoShape* shape, shapes) {
QTransform oldTransform = shape->transformation();
oldTransformations.append(oldTransform);
if (dynamic_cast<const KoShapeGroup*>(shape)) {
newTransformations.append(oldTransform);
} else {
QTransform globalTransform = shape->absoluteTransformation(0);
QTransform localTransform = globalTransform * realTransform * globalTransform.inverted();
newTransformations.append(localTransform*oldTransform);
}
}
return new KoShapeTransformCommand(shapes, oldTransformations, newTransformations);
}
diff --git a/krita/ui/forms/wdggeneralsettings.ui b/krita/ui/forms/wdggeneralsettings.ui
index 999dc26405c..35843ffac7f 100644
--- a/krita/ui/forms/wdggeneralsettings.ui
+++ b/krita/ui/forms/wdggeneralsettings.ui
@@ -1,399 +1,481 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgGeneralSettings</class>
<widget class="QWidget" name="WdgGeneralSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>606</width>
- <height>585</height>
+ <width>720</width>
+ <height>323</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>Cursor Settings</string>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
</property>
- <layout class="QFormLayout" name="formLayout">
- <property name="fieldGrowthPolicy">
- <enum>QFormLayout::ExpandingFieldsGrow</enum>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="textLabel1">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Cursor shape:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="m_cmbCursorShape"/>
- </item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="m_showOutlinePainting">
- <property name="text">
- <string>Show brush outline while painting</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="m_cmbOutlineShape"/>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="textLabel1_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Outline shape:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_2">
- <property name="title">
- <string>Window Settings</string>
- </property>
- <layout class="QFormLayout" name="formLayout_2">
- <item row="0" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Multiple Document Mode:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="m_cmbMDIType">
- <property name="currentIndex">
- <number>1</number>
- </property>
- <item>
- <property name="text">
- <string>Subwindows</string>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Cursor</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Cursor Settings</string>
</property>
- </item>
- <item>
- <property name="text">
- <string>Tabs</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Background Image (overrides color):</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
- <item>
- <widget class="QLabel" name="m_backgroundimage">
- <property name="minimumSize">
- <size>
- <width>200</width>
- <height>0</height>
- </size>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Sunken</enum>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="m_bnFileName">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="clearBgImageButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Clear</string>
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel1">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Cursor shape:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="textLabel1_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Outline shape:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="m_cmbOutlineShape"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="m_showOutlinePainting">
+ <property name="text">
+ <string>Show brush outline while painting</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_cmbCursorShape"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ <zorder>groupBox</zorder>
+ <zorder>verticalSpacer_5</zorder>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>Window</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="1" column="0">
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Window Settings</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Window Background:</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="KColorButton" name="m_mdiColor">
- <property name="text">
- <string/>
- </property>
- <property name="color">
- <color>
- <red>0</red>
- <green>0</green>
- <blue>0</blue>
- </color>
- </property>
- <property name="defaultColor">
- <color>
- <red>0</red>
- <green>0</green>
- <blue>0</blue>
- </color>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QCheckBox" name="m_chkRubberBand">
- <property name="text">
- <string>Don't show contents when moving sub-windows</string>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QCheckBox" name="m_chkCanvasMessages">
- <property name="text">
- <string>Show on-canvas popup messages</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_4">
- <property name="title">
- <string>Tool Options (needs restart)</string>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QRadioButton" name="m_radioToolOptionsInDocker">
- <property name="text">
- <string>In Docker</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QRadioButton" name="m_radioToolOptionsInToolbar">
- <property name="text">
- <string>In Toolbar</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_3">
- <property name="title">
- <string>Miscellaneous</string>
- </property>
- <layout class="QFormLayout" name="formLayout_3">
- <item row="0" column="0">
- <widget class="QCheckBox" name="m_autosaveCheckBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="layoutDirection">
- <enum>Qt::RightToLeft</enum>
- </property>
- <property name="text">
- <string>Autosave every:</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QSpinBox" name="m_autosaveSpinBox">
- <property name="suffix">
- <string> min</string>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>1440</number>
- </property>
- <property name="singleStep">
- <number>5</number>
- </property>
- <property name="value">
- <number>15</number>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Undo stack size:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="m_undoStackSize">
- <property name="minimum">
- <number>0</number>
- </property>
- <property name="maximum">
- <number>1000</number>
- </property>
- <property name="singleStep">
- <number>5</number>
- </property>
- <property name="value">
- <number>30</number>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_3">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Favorite presets:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QSpinBox" name="m_favoritePresetsSpinBox">
- <property name="minimum">
- <number>10</number>
- </property>
- <property name="maximum">
- <number>30</number>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QCheckBox" name="chkShowRootLayer">
- <property name="text">
- <string>Show root layer</string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
- <widget class="QCheckBox" name="m_hideSplashScreen">
- <property name="text">
- <string>Hide splash screen on startup</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="m_chkCompressKra">
- <property name="text">
- <string>Compress .kra files more (slows loading/saving)</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="m_backupFileCheckBox">
- <property name="text">
- <string>Create backup file </string>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Background Image (overrides color):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
+ <item>
+ <widget class="QLabel" name="m_backgroundimage">
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="m_bnFileName">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="clearBgImageButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Window Background:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="m_chkRubberBand">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="m_chkCanvasMessages">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Multiple Document Mode:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="KColorButton" name="m_mdiColor">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="color">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="defaultColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_cmbMDIType">
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Subwindows</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Tabs</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Don't show contents when moving sub-windows:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Show on-canvas popup messages:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="Tools">
+ <attribute name="title">
+ <string>Tools</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Tool Options Location (needs restart)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QRadioButton" name="m_radioToolOptionsInDocker">
+ <property name="text">
+ <string>In Docker</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_radioToolOptionsInToolbar">
+ <property name="text">
+ <string>In Toolbar</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Miscellaneous</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string/>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="m_chkCompressKra">
+ <property name="text">
+ <string>Compress .kra files more (slows loading/saving)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="m_backupFileCheckBox">
+ <property name="text">
+ <string>Create backup file </string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QCheckBox" name="m_hideSplashScreen">
+ <property name="text">
+ <string>Hide splash screen on startup</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QSpinBox" name="m_favoritePresetsSpinBox">
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Undo stack size:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="m_undoStackSize">
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="m_autosaveCheckBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::RightToLeft</enum>
+ </property>
+ <property name="text">
+ <string>Autosave every:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Favorite presets:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="chkShowRootLayer">
+ <property name="text">
+ <string>Show root layer</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="m_autosaveSpinBox">
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>1440</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>15</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
- <item>
+ <item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KColorButton</class>
<extends>QPushButton</extends>
<header>kcolorbutton.h</header>
</customwidget>
</customwidgets>
<resources/>
- <connections>
- <connection>
- <sender>m_autosaveCheckBox</sender>
- <signal>toggled(bool)</signal>
- <receiver>m_autosaveSpinBox</receiver>
- <slot>setEnabled(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>55</x>
- <y>79</y>
- </hint>
- <hint type="destinationlabel">
- <x>168</x>
- <y>79</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/krita/ui/input/config/kis_action_shortcuts_model.cpp b/krita/ui/input/config/kis_action_shortcuts_model.cpp
index d7140faf403..b66330d7286 100644
--- a/krita/ui/input/config/kis_action_shortcuts_model.cpp
+++ b/krita/ui/input/config/kis_action_shortcuts_model.cpp
@@ -1,362 +1,362 @@
/*
* This file is part of the KDE project
* Copyright (C) 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_action_shortcuts_model.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <KLocalizedString>
#include <QMetaClassInfo>
#include <QKeySequence>
#include "KoIcon.h"
#include "input/kis_abstract_input_action.h"
#include "input/kis_input_profile.h"
#include "input/kis_input_profile_manager.h"
#include "input/kis_shortcut_configuration.h"
class KisActionShortcutsModel::Private
{
public:
Private() : action(0), profile(0), temporaryShortcut(0) { }
int shortcutModeCount(uint mode);
KisAbstractInputAction *action;
KisInputProfile *profile;
QList<KisShortcutConfiguration *> shortcuts;
KisShortcutConfiguration *temporaryShortcut;
};
KisActionShortcutsModel::KisActionShortcutsModel(QObject *parent)
: QAbstractListModel(parent), d(new Private)
{
connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), SLOT(currentProfileChanged()));
}
KisActionShortcutsModel::~KisActionShortcutsModel()
{
}
QVariant KisActionShortcutsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() == d->shortcuts.count() && role == Qt::DisplayRole) {
if (index.column() == 0) {
return i18n("Add shortcut...");
}
else {
return QVariant();
}
}
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0:
switch (d->shortcuts.at(index.row())->type()) {
case KisShortcutConfiguration::KeyCombinationType:
return i18nc("Shortcut type", "Key Combination");
case KisShortcutConfiguration::MouseButtonType:
return i18nc("Shortcut type", "Mouse Button");
case KisShortcutConfiguration::MouseWheelType:
return i18nc("Shortcut type", "Mouse Wheel");
case KisShortcutConfiguration::GestureType:
return i18nc("Shortcut type", "Gesture");
default:
return i18n("Unknown Input");
}
break;
case 1: {
KisShortcutConfiguration *s = d->shortcuts.at(index.row());
QString output;
switch (s->type()) {
case KisShortcutConfiguration::KeyCombinationType:
output = KisShortcutConfiguration::keysToText(s->keys());
break;
case KisShortcutConfiguration::MouseButtonType:
output = KisShortcutConfiguration::buttonsInputToText(
s->keys(), s->buttons());
break;
case KisShortcutConfiguration::MouseWheelType:
output = KisShortcutConfiguration::wheelInputToText(
s->keys(), s->wheel());
break;
default:
break;
}
return output;
}
case 2:
return d->action->shortcutIndexes().key(d->shortcuts.at(index.row())->mode());
case 3:
return themedIcon("edit-delete");
default:
break;
}
}
else if (role == Qt::EditRole) {
KisShortcutConfiguration *s;
if (index.row() == d->shortcuts.count()) {
if (!d->temporaryShortcut) {
d->temporaryShortcut = new KisShortcutConfiguration;
}
s = d->temporaryShortcut;
}
else {
s = d->shortcuts.at(index.row());
}
switch (index.column()) {
case 0:
return s->type();
case 1:
return QVariant::fromValue(s);
case 2:
return s->mode();
default:
break;
}
}
return QVariant();
}
int KisActionShortcutsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return d->shortcuts.count() + 1;
}
int KisActionShortcutsModel::columnCount(const QModelIndex & /*parent*/) const
{
return 3;
}
QVariant KisActionShortcutsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
return QVariant();
}
switch (section) {
case 0:
return i18nc("Type of shortcut", "Type");
case 1:
return i18nc("Input for shortcut", "Input");
case 2:
return i18nc("Action to trigger with shortcut", "Action");
default:
break;
}
return QVariant();
}
Qt::ItemFlags KisActionShortcutsModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) {
return Qt::ItemIsEnabled;
}
if (index.row() == d->shortcuts.count() && index.column() != 0) {
return Qt::ItemIsEnabled;
}
if (index.row() >= d->shortcuts.count()) {
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
KisShortcutConfiguration* config = d->shortcuts.at(index.row());
if (index.column() == 2 && d->action->isShortcutRequired(config->mode()) && d->shortcutModeCount(config->mode()) < 2) {
return Qt::ItemIsSelectable;
}
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
bool KisActionShortcutsModel::canRemoveRow(int row) const
{
KisShortcutConfiguration* config = d->shortcuts.at(row);
return !(d->action->isShortcutRequired(config->mode()) && d->shortcutModeCount(config->mode()) < 2);
}
bool KisActionShortcutsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::EditRole) {
return false;
}
if (index.row() == d->shortcuts.count()) {
if (!d->temporaryShortcut || (index.column() == 0 && value.toUInt() == 0)) {
return false;
}
beginInsertRows(QModelIndex(), d->shortcuts.count(), d->shortcuts.count());
d->temporaryShortcut->setAction(d->action);
d->profile->addShortcut(d->temporaryShortcut);
d->shortcuts.append(d->temporaryShortcut);
d->temporaryShortcut = 0;
endInsertRows();
}
switch (index.column()) {
case 0:
d->shortcuts.at(index.row())->setType(static_cast<KisShortcutConfiguration::ShortcutType>(value.toUInt()));
break;
case 1: {
KisShortcutConfiguration *newData = value.value<KisShortcutConfiguration *>();
KisShortcutConfiguration *oldData = d->shortcuts.at(index.row());
if (newData == oldData)
return true;
oldData->setKeys(newData->keys());
oldData->setButtons(newData->buttons());
oldData->setWheel(newData->wheel());
oldData->setGesture(newData->gesture());
break;
}
case 2:
d->shortcuts.at(index.row())->setMode(value.toUInt());
break;
}
return true;
}
KisAbstractInputAction *KisActionShortcutsModel::action() const
{
return d->action;
}
void KisActionShortcutsModel::setAction(KisAbstractInputAction *action)
{
if (action != d->action) {
if (d->action) {
beginRemoveRows(QModelIndex(), 0, d->shortcuts.count() - 1);
endRemoveRows();
}
d->action = action;
if (d->action && d->profile) {
d->shortcuts = d->profile->shortcutsForAction(d->action);
beginInsertRows(QModelIndex(), 0, d->shortcuts.count() - 1);
endInsertRows();
}
}
}
KisInputProfile *KisActionShortcutsModel::profile() const
{
return d->profile;
}
void KisActionShortcutsModel::setProfile(KisInputProfile *profile)
{
if (profile != d->profile) {
if (d->profile) {
beginRemoveRows(QModelIndex(), 0, d->shortcuts.count() - 1);
endRemoveRows();
}
d->profile = profile;
if (d->action && d->profile) {
d->shortcuts = d->profile->shortcutsForAction(d->action);
beginInsertRows(QModelIndex(), 0, d->shortcuts.count() - 1);
endInsertRows();
}
}
}
void KisActionShortcutsModel::currentProfileChanged()
{
setProfile(KisInputProfileManager::instance()->currentProfile());
}
bool KisActionShortcutsModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (row < 0 || row >= d->shortcuts.count() || count == 0) {
return false;
}
beginRemoveRows(parent, row, row + count - 1);
for (int i = row; i < d->shortcuts.count() && count > 0; ++i, count--) {
KisShortcutConfiguration *s = d->shortcuts.at(i);
if (!d->action->isShortcutRequired(s->mode()) && d->shortcutModeCount(s->mode()) < 2) {
continue;
}
d->profile->removeShortcut(s);
d->shortcuts.removeOne(s);
delete s;
}
endRemoveRows();
return true;
}
int KisActionShortcutsModel::Private::shortcutModeCount(uint mode)
{
int count = 0;
foreach(KisShortcutConfiguration* s, shortcuts) {
if(s->mode() == mode) {
count++;
}
}
return count;
}
diff --git a/krita/ui/input/kis_abstract_input_action.cpp b/krita/ui/input/kis_abstract_input_action.cpp
index 5a9bfe2533b..496b4567e7f 100644
--- a/krita/ui/input/kis_abstract_input_action.cpp
+++ b/krita/ui/input/kis_abstract_input_action.cpp
@@ -1,158 +1,202 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_abstract_input_action.h"
#include <QPointF>
#include <QMouseEvent>
#include <klocalizedstring.h>
+#include <QDebug>
class Q_DECL_HIDDEN KisAbstractInputAction::Private
{
public:
QString id;
QString name;
QString description;
QHash<QString, int> indexes;
- QPointF lastMousePosition;
+ QPointF lastCursorPosition;
static KisInputManager* inputManager;
};
KisInputManager *KisAbstractInputAction::Private::inputManager = 0;
KisAbstractInputAction::KisAbstractInputAction(const QString & id)
: d(new Private)
{
d->id = id;
d->indexes.insert(i18n("Activate"), 0);
}
KisAbstractInputAction::~KisAbstractInputAction()
{
delete d;
}
void KisAbstractInputAction::activate(int shortcut)
{
Q_UNUSED(shortcut);
}
void KisAbstractInputAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
}
void KisAbstractInputAction::begin(int shortcut, QEvent *event)
{
Q_UNUSED(shortcut);
- QMouseEvent *mouseEvent;
- if (event && (mouseEvent = dynamic_cast<QMouseEvent*>(event))) {
- d->lastMousePosition = mouseEvent->posF();
+ if (event) {
+ d->lastCursorPosition = eventPosF(event);
}
}
void KisAbstractInputAction::inputEvent(QEvent* event)
{
- QMouseEvent *mouseEvent;
- if (event && (mouseEvent = dynamic_cast<QMouseEvent*>(event))) {
- if (mouseEvent->type() == QEvent::MouseMove) {
- mouseMoved(d->lastMousePosition, mouseEvent->posF());
- }
- d->lastMousePosition = mouseEvent->posF();
+ if (event) {
+ QPointF newPosition = eventPosF(event);
+ cursorMoved(d->lastCursorPosition, newPosition);
+ d->lastCursorPosition = newPosition;
}
}
void KisAbstractInputAction::end(QEvent *event)
{
Q_UNUSED(event);
}
-void KisAbstractInputAction::mouseMoved(const QPointF &lastPos, const QPointF &pos)
+void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
Q_UNUSED(lastPos);
Q_UNUSED(pos);
}
bool KisAbstractInputAction::supportsHiResInputEvents() const
{
return false;
}
KisInputManager* KisAbstractInputAction::inputManager() const
{
return Private::inputManager;
}
QString KisAbstractInputAction::name() const
{
return d->name;
}
QString KisAbstractInputAction::description() const
{
return d->description;
}
int KisAbstractInputAction::priority() const
{
return 0;
}
bool KisAbstractInputAction::canIgnoreModifiers() const
{
return false;
}
QHash< QString, int > KisAbstractInputAction::shortcutIndexes() const
{
return d->indexes;
}
QString KisAbstractInputAction::id() const
{
return d->id;
}
void KisAbstractInputAction::setName(const QString& name)
{
d->name = name;
}
void KisAbstractInputAction::setDescription(const QString& description)
{
d->description = description;
}
void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int >& indexes)
{
d->indexes = indexes;
}
void KisAbstractInputAction::setInputManager(KisInputManager *manager)
{
Private::inputManager = manager;
}
bool KisAbstractInputAction::isShortcutRequired(int shortcut) const
{
Q_UNUSED(shortcut);
return false;
}
+
+
+QPoint KisAbstractInputAction::eventPos(const QEvent *event) {
+
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ return static_cast<const QMouseEvent*>(event)->pos();
+
+ case QEvent::TabletMove:
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ return static_cast<const QTabletEvent*>(event)->pos();
+
+ case QEvent::Wheel:
+ return static_cast<const QWheelEvent*>(event)->pos();
+
+ default:
+ qCritical() << "KisAbstractInputAction tried to process event data from an unhandled event type" << event->type();
+ return QPoint();
+ }
+}
+
+
+QPointF KisAbstractInputAction::eventPosF(const QEvent *event) {
+
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ return static_cast<const QMouseEvent*>(event)->posF();
+
+ case QEvent::TabletMove:
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ return static_cast<const QTabletEvent*>(event)->posF();
+
+ case QEvent::Wheel:
+ return static_cast<const QWheelEvent*>(event)->posF();
+
+ default:
+ qCritical() << "KisAbstractInputAction tried to process event data from an unhandled event type" << event->type();
+ return QPoint();
+ }
+}
diff --git a/krita/ui/input/kis_abstract_input_action.h b/krita/ui/input/kis_abstract_input_action.h
index fb30e3df29e..93b2b1706da 100644
--- a/krita/ui/input/kis_abstract_input_action.h
+++ b/krita/ui/input/kis_abstract_input_action.h
@@ -1,202 +1,215 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ABSTRACT_INPUT_ACTION_H
#define KIS_ABSTRACT_INPUT_ACTION_H
#include <QHash>
+#include <QPoint>
#include "kritaui_export.h"
class QPointF;
class QEvent;
class KisInputManager;
/**
* \brief Abstract base class for input actions.
*
* Input actions represent actions to be performed when interacting
* with the canvas. They are managed by KisInputManager and activated
* when KisKeyShortcut or KisStrokeShortcut detects it matches a certain
* set of inputs.
*
* The begin() method uses an index for the type of behaviour to activate.
* This index can be used to trigger behaviour when different events occur.
*
* The events can be of two types:
* 1) Key events. The input manager calls begin() and end() sequentially
* with an \p index parameter to begin() representing the type of
* action that should be performed. The \p event parameter of both
* calls in null.
* 2) Stroke events. The input manager calls begin() and end() on the
* corresponding mouse down and up events. The \p event parameter
* will be of QMouseEvent type, representing the event happened.
* All the mouse move events between begin() and end() will be
* redirected to the inputEvent() method.
- *
- * You can fetch the QTabletEvent data for the current mouse event
- * with inputManager()->lastTabletEvent().
*/
class KRITAUI_EXPORT KisAbstractInputAction
{
public:
/**
* Constructor.
*
* \param manager The InputManager this action belongs to.
*/
explicit KisAbstractInputAction(const QString &id);
/**
* Destructor.
*/
virtual ~KisAbstractInputAction();
/**
* The method is called when the action is yet to be started,
* that is, e.g. the user has pressed all the modifiers for the
* action but hasn't started painting yet. This method is a right
* place to show the user what is going to happen, e.g. change the
* cursor.
*/
virtual void activate(int shortcut);
/**
* The method is called when the action is not a candidate for
* the starting anymore. The action should revert everything that
* was done in activate() method.
*
* \see activate()
*/
virtual void deactivate(int shortcut);
/**
* Begin the action.
*
* \param shortcut The index of the behaviour to trigger.
* \param event The mouse event that has triggered this action.
* Is null for keyboard-activated actions.
*/
virtual void begin(int shortcut, QEvent *event);
/**
* End the action.
* \param event The mouse event that has finished this action.
* Is null for keyboard-activated actions.
*/
virtual void end(QEvent *event);
/**
* Process an input event.
*
* By default handles MouseMove events and passes the data to
- * a convenience mouseMoved() method
+ * a convenience cursorMoved() method
*
* \param event An event to process.
*/
virtual void inputEvent(QEvent* event);
/**
* Returns true if the action can handle HiRes flow of move events
* which is generated by the tablet. If the function returns
* false, some of the events will be dropped or postponed. For
* most of the actions in Krita (except of real painting) it is
* perfectly acceptable, so 'false' is the default value.
*/
virtual bool supportsHiResInputEvents() const;
/**
* The indexes of shortcut behaviours available.
*/
virtual QHash<QString, int> shortcutIndexes() const;
/**
* The id of this action.
*/
virtual QString id() const;
/**
* The translated name of this action.
*/
virtual QString name() const;
/**
* A short description of this action.
*/
virtual QString description() const;
/**
* The priority for this action.
*
* Priority determines how "important" the action is and is used
* to resolve conflicts when multiple actions can be activated.
*/
virtual int priority() const;
/**
* Returns true if an action can run with any modifiers pressed
* (the shortcut's modifiers list must be empty for that). That is
* used for making one type of actions default one.
*/
virtual bool canIgnoreModifiers() const;
/**
* Return true when the specified shortcut is required for basic
* user interaction. This is used by the configuration system to
* prevent basic actions like painting from being removed.
*
* \param shortcut The shortcut index to check.
* \return True if the shortcut is required, false if not.
*/
virtual bool isShortcutRequired(int shortcut) const;
protected:
/**
* The input manager this action belongs to.
*/
KisInputManager *inputManager() const;
/**
* Set the name of this action.
*
* \param name The new name.
*/
void setName(const QString &name);
/**
* Set the description of this action.
*
* \param description The new description.
*/
void setDescription(const QString &description);
/**
* Set the available indexes of shortcut behaviours.
*
* \param indexes The new indexes.
*/
void setShortcutIndexes(const QHash<QString, int> &indexes);
/**
- * Convenience method for handling the mouse moves. It is
- * called by the default implementation of inputEvent
+ * Convenience method for handling cursor movement for tablet, mouse and touch.
+ * The default implementation of inputEvent calls this function.
+ */
+ virtual void cursorMoved(const QPointF &lastPos, const QPointF &pos);
+
+ /**
+ * Convenience method to extract the position from a cursor movement event.
+ *
+ * \param event A mouse or tablet event.
+ */
+ static QPoint eventPos(const QEvent *event);
+
+ /**
+ * Convenience method to extract the floating point position from a
+ * cursor movement event.
+ *
+ * \param event A mouse or tablet event.
*/
- virtual void mouseMoved(const QPointF &lastPos, const QPointF &pos);
+ static QPointF eventPosF(const QEvent *event);
private:
friend class KisInputManager;
static void setInputManager(KisInputManager *manager);
class Private;
Private * const d;
};
#endif // KIS_ABSTRACT_INPUT_ACTION_H
diff --git a/krita/ui/input/kis_alternate_invocation_action.cpp b/krita/ui/input/kis_alternate_invocation_action.cpp
index 09107ce6e83..2a5d067ddf7 100644
--- a/krita/ui/input/kis_alternate_invocation_action.cpp
+++ b/krita/ui/input/kis_alternate_invocation_action.cpp
@@ -1,169 +1,160 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_alternate_invocation_action.h"
#include <QApplication>
#include <klocalizedstring.h>
#include <kis_tool_proxy.h>
#include <kis_canvas2.h>
#include "kis_input_manager.h"
#include "kis_tool.h"
#include "kis_cursor.h"
struct KisAlternateInvocationAction::Private
{
KisTool::ToolAction savedAction;
};
KisAlternateInvocationAction::KisAlternateInvocationAction()
: KisAbstractInputAction("Alternate Invocation")
, m_d(new Private)
{
setName(i18n("Alternate Invocation"));
setDescription(i18n("The <i>Alternate Invocation</i> action performs an alternate action with the current tool. For example, using the brush tool it picks a color from the canvas."));
QHash<QString, int> shortcuts;
shortcuts.insert(i18n("Primary Mode"), PrimaryAlternateModeShortcut);
shortcuts.insert(i18n("Secondary Mode"), SecondaryAlternateModeShortcut);
shortcuts.insert(i18n("Pick Foreground Color from Current Layer"), PickColorFgLayerModeShortcut);
shortcuts.insert(i18n("Pick Background Color from Current Layer"), PickColorBgLayerModeShortcut);
shortcuts.insert(i18n("Pick Foreground Color from Merged Image"), PickColorFgImageModeShortcut);
shortcuts.insert(i18n("Pick Background Color from Merged Image"), PickColorBgImageModeShortcut);
setShortcutIndexes(shortcuts);
}
KisAlternateInvocationAction::~KisAlternateInvocationAction()
{
}
KisTool::ToolAction KisAlternateInvocationAction::shortcutToToolAction(int shortcut)
{
KisTool::ToolAction action = KisTool::Alternate_NONE;
switch ((Shortcut)shortcut) {
case PickColorFgLayerModeShortcut:
action = KisTool::AlternatePickFgNode;
break;
case PickColorBgLayerModeShortcut:
action = KisTool::AlternatePickBgNode;
break;
case PickColorFgImageModeShortcut:
action = KisTool::AlternatePickFgImage;
break;
case PickColorBgImageModeShortcut:
action = KisTool::AlternatePickBgImage;
break;
case PrimaryAlternateModeShortcut:
action = KisTool::AlternateSecondary;
break;
case SecondaryAlternateModeShortcut:
action = KisTool::AlternateThird;
break;
}
return action;
}
void KisAlternateInvocationAction::activate(int shortcut)
{
KisTool::ToolAction action = shortcutToToolAction(shortcut);
inputManager()->toolProxy()->activateToolAction(action);
}
void KisAlternateInvocationAction::deactivate(int shortcut)
{
KisTool::ToolAction action = shortcutToToolAction(shortcut);
inputManager()->toolProxy()->deactivateToolAction(action);
}
int KisAlternateInvocationAction::priority() const
{
return 9;
}
void KisAlternateInvocationAction::begin(int shortcut, QEvent *event)
{
if (!event) return;
KisAbstractInputAction::begin(shortcut, event);
- QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
- QMouseEvent targetEvent(QEvent::MouseButtonPress, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
+ QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); // There must be a better way
m_d->savedAction = shortcutToToolAction(shortcut);
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event);
}
void KisAlternateInvocationAction::end(QEvent *event)
{
if (!event) return;
- QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
-
- QMouseEvent targetEvent(*mouseEvent);
+ Qt::KeyboardModifiers modifiers;
switch (m_d->savedAction) {
case KisTool::AlternatePickFgNode:
- targetEvent = QMouseEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
+ modifiers = Qt::ControlModifier;
break;
case KisTool::AlternateThird:
- targetEvent = QMouseEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier | Qt::AltModifier);
+ modifiers = Qt::ControlModifier | Qt::AltModifier;
break;
default:
;
}
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::END, m_d->savedAction, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ QMouseEvent targetEvent = QMouseEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers);
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, m_d->savedAction, &targetEvent, event);
KisAbstractInputAction::end(event);
}
void KisAlternateInvocationAction::inputEvent(QEvent* event)
{
- if (event && event->type() == QEvent::MouseMove) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
-
- QMouseEvent targetEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier);
-
+ if (!event || ((event->type() != QEvent::MouseMove) && (event->type() != QEvent::TabletMove))) {
+ Qt::KeyboardModifiers modifiers;
switch (m_d->savedAction) {
case KisTool::AlternatePickFgNode:
- targetEvent = QMouseEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
+ modifiers = Qt::ControlModifier;
break;
case KisTool::AlternateThird:
- targetEvent = QMouseEvent(QEvent::MouseMove, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier | Qt::AltModifier);
+ modifiers = Qt::ControlModifier | Qt::AltModifier;
break;
default:
- ;
+ modifiers = Qt::ShiftModifier;
}
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ QMouseEvent targetEvent(QEvent::MouseMove, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers);
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event);
}
+
}
diff --git a/krita/ui/input/kis_change_primary_setting_action.cpp b/krita/ui/input/kis_change_primary_setting_action.cpp
index 0e32866a8b4..b15de7c1acd 100644
--- a/krita/ui/input/kis_change_primary_setting_action.cpp
+++ b/krita/ui/input/kis_change_primary_setting_action.cpp
@@ -1,99 +1,88 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_change_primary_setting_action.h"
#include <klocalizedstring.h>
#include "kis_input_manager.h"
#include "kis_canvas2.h"
#include "kis_tool_proxy.h"
#include <QApplication>
#include "kis_cursor.h"
KisChangePrimarySettingAction::KisChangePrimarySettingAction()
: KisAbstractInputAction("Change Primary Setting")
{
setName(i18n("Change Primary Setting"));
setDescription(i18n("The <i>Change Primary Setting</i> action changes a tool's \"Primary Setting\", for example the brush size for the brush tool."));
}
KisChangePrimarySettingAction::~KisChangePrimarySettingAction()
{
}
void KisChangePrimarySettingAction::activate(int shortcut)
{
Q_UNUSED(shortcut);
inputManager()->toolProxy()->activateToolAction(KisTool::AlternateChangeSize);
}
void KisChangePrimarySettingAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
inputManager()->toolProxy()->deactivateToolAction(KisTool::AlternateChangeSize);
}
int KisChangePrimarySettingAction::priority() const
{
return 8;
}
void KisChangePrimarySettingAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
- QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
- if (mouseEvent) {
- QMouseEvent targetEvent(QEvent::MouseButtonPress, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier);
+ if (event) {
+ QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier);
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::BEGIN, KisTool::AlternateChangeSize, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, KisTool::AlternateChangeSize, &targetEvent, event);
}
}
void KisChangePrimarySettingAction::end(QEvent *event)
{
- QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
- if (mouseEvent) {
- QMouseEvent targetEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier);
-
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::END, KisTool::AlternateChangeSize, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ if (event) {
+ QMouseEvent targetEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier);
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, KisTool::AlternateChangeSize, &targetEvent, event);
}
KisAbstractInputAction::end(event);
}
void KisChangePrimarySettingAction::inputEvent(QEvent* event)
{
- if (event && event->type() == QEvent::MouseMove) {
- QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
-
- QMouseEvent targetEvent(QEvent::MouseButtonRelease, mouseEvent->pos(), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier);
-
- inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::CONTINUE, KisTool::AlternateChangeSize, &targetEvent, event,
- inputManager()->lastTabletEvent());
+ // Is there a reason to restrict to only mouse and tablet events?
+ if (event && (event->type() != QEvent::MouseMove && event->type() != QEvent::TabletMove)) {
+ QMouseEvent targetEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier);
+ inputManager()->toolProxy()->forwardEvent(KisToolProxy::CONTINUE, KisTool::AlternateChangeSize, &targetEvent, event);
}
}
diff --git a/krita/ui/input/kis_extended_modifiers_mapper.cpp b/krita/ui/input/kis_extended_modifiers_mapper.cpp
index 3e1925f3da7..1a506913de4 100644
--- a/krita/ui/input/kis_extended_modifiers_mapper.cpp
+++ b/krita/ui/input/kis_extended_modifiers_mapper.cpp
@@ -1,166 +1,166 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_extended_modifiers_mapper.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QApplication>
#ifdef HAVE_X11
#include <QX11Info>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
struct KeyMapping {
KeyMapping() {}
KeyMapping(KeySym sym, Qt::Key key) : x11KeySym(sym), qtKey(key) {}
KeySym x11KeySym;
Qt::Key qtKey;
};
#endif /* HAVE_X11 */
struct KisExtendedModifiersMapper::Private
{
Private();
#ifdef HAVE_X11
QVector<KeyMapping> mapping;
char keysState[32];
bool checkKeyCodePressedX11(KeyCode key);
bool checkKeySymPressedX11(KeySym sym);
#endif /* HAVE_X11 */
};
#ifdef HAVE_X11
KisExtendedModifiersMapper::Private::Private()
{
XQueryKeymap(QX11Info::display(), keysState);
mapping.append(KeyMapping(XK_Shift_L, Qt::Key_Shift));
mapping.append(KeyMapping(XK_Shift_R, Qt::Key_Shift));
mapping.append(KeyMapping(XK_Control_L, Qt::Key_Control));
mapping.append(KeyMapping(XK_Control_R, Qt::Key_Control));
mapping.append(KeyMapping(XK_Meta_L, Qt::Key_Meta));
mapping.append(KeyMapping(XK_Meta_R, Qt::Key_Meta));
mapping.append(KeyMapping(XK_Super_L, Qt::Key_Super_L));
mapping.append(KeyMapping(XK_Super_R, Qt::Key_Super_R));
mapping.append(KeyMapping(XK_Hyper_L, Qt::Key_Hyper_L));
mapping.append(KeyMapping(XK_Hyper_R, Qt::Key_Hyper_R));
mapping.append(KeyMapping(XK_space, Qt::Key_Space));
for (int qtKey = Qt::Key_0, x11Sym = XK_0;
qtKey <= Qt::Key_9;
qtKey++, x11Sym++) {
mapping.append(KeyMapping(x11Sym, Qt::Key(qtKey)));
}
for (int qtKey = Qt::Key_A, x11Sym = XK_a;
qtKey <= Qt::Key_Z;
qtKey++, x11Sym++) {
mapping.append(KeyMapping(x11Sym, Qt::Key(qtKey)));
}
}
bool KisExtendedModifiersMapper::Private::checkKeyCodePressedX11(KeyCode key)
{
int byte = key / 8;
char mask = 1 << (key % 8);
return keysState[byte] & mask;
}
bool KisExtendedModifiersMapper::Private::checkKeySymPressedX11(KeySym sym)
{
KeyCode key = XKeysymToKeycode(QX11Info::display(), sym);
return key != 0 ? checkKeyCodePressedX11(key) : false;
}
#else /* HAVE_X11 */
KisExtendedModifiersMapper::Private::Private()
{
}
#endif /* HAVE_X11 */
KisExtendedModifiersMapper::KisExtendedModifiersMapper()
: m_d(new Private)
{
}
KisExtendedModifiersMapper::~KisExtendedModifiersMapper()
{
}
KisExtendedModifiersMapper::ExtendedModifiers
KisExtendedModifiersMapper::queryExtendedModifiers()
{
ExtendedModifiers modifiers;
#ifdef HAVE_X11
foreach (const KeyMapping &map, m_d->mapping) {
if (m_d->checkKeySymPressedX11(map.x11KeySym)) {
modifiers << map.qtKey;
}
}
#else /* HAVE_X11 */
Qt::KeyboardModifiers standardModifiers = queryStandardModifiers();
if (standardModifiers & Qt::ShiftModifier) {
modifiers << Qt::Key_Shift;
}
if (standardModifiers & Qt::ControlModifier) {
modifiers << Qt::Key_Control;
}
if (standardModifiers & Qt::AltModifier) {
modifiers << Qt::Key_Alt;
}
if (standardModifiers & Qt::MetaModifier) {
modifiers << Qt::Key_Meta;
}
#endif /* HAVE_X11 */
return modifiers;
}
Qt::KeyboardModifiers KisExtendedModifiersMapper::queryStandardModifiers()
{
return QApplication::queryKeyboardModifiers();
}
diff --git a/krita/ui/input/kis_gamma_exposure_action.cpp b/krita/ui/input/kis_gamma_exposure_action.cpp
index be8f18a6438..bc61d595567 100644
--- a/krita/ui/input/kis_gamma_exposure_action.cpp
+++ b/krita/ui/input/kis_gamma_exposure_action.cpp
@@ -1,199 +1,199 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_gamma_exposure_action.h"
#include <QApplication>
#include <QIcon>
#include <klocale.h>
#include <kis_canvas2.h>
#include "kis_cursor.h"
#include "KisViewManager.h"
#include "kis_input_manager.h"
#include "krita_utils.h"
#include "kis_exposure_gamma_correction_interface.h"
class KisGammaExposureAction::Private
{
public:
Private(KisGammaExposureAction *qq) : q(qq), baseExposure(0.0), baseGamma(0.0) {}
KisGammaExposureAction *q;
Shortcuts mode;
qreal baseExposure;
qreal baseGamma;
void addExposure(qreal diff);
void addGamma(qreal diff);
};
void KisGammaExposureAction::Private::addExposure(qreal diff)
{
KisExposureGammaCorrectionInterface *interface =
q->inputManager()->canvas()->exposureGammaCorrectionInterface();
if (!interface->canChangeExposureAndGamma()) return;
interface->setCurrentExposure(interface->currentExposure() + diff);
}
void KisGammaExposureAction::Private::addGamma(qreal diff)
{
KisExposureGammaCorrectionInterface *interface =
q->inputManager()->canvas()->exposureGammaCorrectionInterface();
if (!interface->canChangeExposureAndGamma()) return;
interface->setCurrentGamma(interface->currentGamma() + diff);
}
KisGammaExposureAction::KisGammaExposureAction()
: KisAbstractInputAction("Exposure or Gamma")
, d(new Private(this))
{
setName(i18n("Exposure and Gamma"));
setDescription(i18n("The <i>Exposure and Gamma</i> action changes the display mode of the canvas."));
QHash< QString, int > shortcuts;
shortcuts.insert(i18n("Exposure Mode"), ExposureShortcut);
shortcuts.insert(i18n("Gamma Mode"), GammaShortcut);
shortcuts.insert(i18n("Exposure +0.5"), AddExposure05Shortcut);
shortcuts.insert(i18n("Exposure -0.5"), RemoveExposure05Shortcut);
shortcuts.insert(i18n("Gamma +0.5"), AddGamma05Shortcut);
shortcuts.insert(i18n("Gamma -0.5"), RemoveGamma05Shortcut);
shortcuts.insert(i18n("Exposure +0.2"), AddExposure02Shortcut);
shortcuts.insert(i18n("Exposure -0.2"), RemoveExposure02Shortcut);
shortcuts.insert(i18n("Gamma +0.2"), AddGamma02Shortcut);
shortcuts.insert(i18n("Gamma -0.2"), RemoveGamma02Shortcut);
shortcuts.insert(i18n("Reset Exposure and Gamma"), ResetExposureAndGammaShortcut);
setShortcutIndexes(shortcuts);
}
KisGammaExposureAction::~KisGammaExposureAction()
{
delete d;
}
int KisGammaExposureAction::priority() const
{
return 5;
}
void KisGammaExposureAction::activate(int shortcut)
{
if (shortcut == ExposureShortcut) {
QApplication::setOverrideCursor(KisCursor::changeExposureCursor());
} else /* if (shortcut == GammaShortcut) */ {
QApplication::setOverrideCursor(KisCursor::changeGammaCursor());
}
}
void KisGammaExposureAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisGammaExposureAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
KisExposureGammaCorrectionInterface *interface =
inputManager()->canvas()->exposureGammaCorrectionInterface();
switch(shortcut) {
case ExposureShortcut:
d->baseExposure = interface->currentExposure();
d->mode = (Shortcuts)shortcut;
break;
case GammaShortcut:
d->baseGamma = interface->currentGamma();
d->mode = (Shortcuts)shortcut;
break;
case AddExposure05Shortcut:
d->addExposure(0.5);
break;
case RemoveExposure05Shortcut:
d->addExposure(-0.5);
break;
case AddGamma05Shortcut:
d->addGamma(0.5);
break;
case RemoveGamma05Shortcut:
d->addGamma(-0.5);
break;
case AddExposure02Shortcut:
d->addExposure(0.2);
break;
case RemoveExposure02Shortcut:
d->addExposure(-0.2);
break;
case AddGamma02Shortcut:
d->addGamma(0.2);
break;
case RemoveGamma02Shortcut:
d->addGamma(-0.2);
break;
case ResetExposureAndGammaShortcut: {
KisExposureGammaCorrectionInterface *interface =
inputManager()->canvas()->exposureGammaCorrectionInterface();
if (!interface->canChangeExposureAndGamma()) break;
interface->setCurrentGamma(1.0);
interface->setCurrentExposure(0.0);
break;
}
}
}
-void KisGammaExposureAction::mouseMoved(const QPointF &lastPos, const QPointF &pos)
+void KisGammaExposureAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
QPointF diff = -(pos - lastPos);
const int step = 200;
KisExposureGammaCorrectionInterface *interface =
inputManager()->canvas()->exposureGammaCorrectionInterface();
if (!interface->canChangeExposureAndGamma()) return;
if (d->mode == ExposureShortcut) {
d->baseExposure += qreal(diff.y()) / step;
interface->setCurrentExposure(d->baseExposure);
} else if (d->mode == GammaShortcut) {
d->baseGamma += qreal(diff.y()) / step;
interface->setCurrentGamma(d->baseGamma);
}
}
bool KisGammaExposureAction::isShortcutRequired(int shortcut) const
{
Q_UNUSED(shortcut);
return false;
}
diff --git a/krita/ui/input/kis_gamma_exposure_action.h b/krita/ui/input/kis_gamma_exposure_action.h
index 99991e84e15..23b63a7d09d 100644
--- a/krita/ui/input/kis_gamma_exposure_action.h
+++ b/krita/ui/input/kis_gamma_exposure_action.h
@@ -1,62 +1,62 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_GAMMA_EXPOSURE_ACTION_H
#define __KIS_GAMMA_EXPOSURE_ACTION_H
#include "kis_abstract_input_action.h"
class KisGammaExposureAction : public KisAbstractInputAction
{
public:
/**
* The different behaviours for this action.
*/
enum Shortcuts {
ExposureShortcut,
GammaShortcut,
AddExposure05Shortcut,
RemoveExposure05Shortcut,
AddGamma05Shortcut,
RemoveGamma05Shortcut,
AddExposure02Shortcut,
RemoveExposure02Shortcut,
AddGamma02Shortcut,
RemoveGamma02Shortcut,
ResetExposureAndGammaShortcut
};
explicit KisGammaExposureAction();
virtual ~KisGammaExposureAction();
virtual int priority() const;
void activate(int shortcut);
void deactivate(int shortcut);
void begin(int shortcut, QEvent *event = 0);
- void mouseMoved(const QPointF &lastPos, const QPointF &pos);
+ void cursorMoved(const QPointF &lastPos, const QPointF &pos);
bool isShortcutRequired(int shortcut) const;
private:
class Private;
Private * const d;
};
#endif /* __KIS_GAMMA_EXPOSURE_ACTION_H */
diff --git a/krita/ui/input/kis_input_manager.cpp b/krita/ui/input/kis_input_manager.cpp
index 5a147bfed24..7c6f64d4121 100644
--- a/krita/ui/input/kis_input_manager.cpp
+++ b/krita/ui/input/kis_input_manager.cpp
@@ -1,990 +1,484 @@
-/* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
+/* This file is part of the KDE project
+ *
+ * Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
+ * Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_input_manager.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QQueue>
-#include <QMessageBox>
-
#include <klocalizedstring.h>
#include <QApplication>
#include <KoToolManager.h>
#include "kis_tool_proxy.h"
#include <kis_config.h>
#include <kis_canvas2.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_canvas_resource_provider.h>
#include <kis_favorite_resource_manager.h>
#include "kis_abstract_input_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_pan_action.h"
#include "kis_alternate_invocation_action.h"
#include "kis_rotate_canvas_action.h"
#include "kis_zoom_action.h"
#include "kis_show_palette_action.h"
#include "kis_change_primary_setting_action.h"
#include "kis_shortcut_matcher.h"
#include "kis_stroke_shortcut.h"
#include "kis_single_action_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_input_profile.h"
#include "kis_input_profile_manager.h"
#include "kis_shortcut_configuration.h"
#include <input/kis_tablet_debugger.h>
-#include <input/kis_tablet_event.h>
#include <kis_signal_compressor.h>
#include "kis_extended_modifiers_mapper.h"
+#include "kis_input_manager_p.h"
template <typename T>
uint qHash(QPointer<T> value) {
return reinterpret_cast<quintptr>(value.data());
}
-class Q_DECL_HIDDEN KisInputManager::Private
-{
-public:
- Private(KisInputManager *qq)
- : q(qq)
- , toolProxy(0)
- , forwardAllEventsToTool(false)
- , ignoreQtCursorEvents(false)
- , disableTouchOnCanvas(false)
- , touchHasBlockedPressEvents(false)
- #ifdef HAVE_X11
- , hiResEventsWorkaroundCoeff(1.0, 1.0)
- #endif
- , lastTabletEvent(0)
- , lastTouchEvent(0)
- , defaultInputAction(0)
- , eventsReceiver(0)
- , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE)
- , testingAcceptCompressedTabletEvents(false)
- , testingCompressBrushEvents(false)
- , focusOnEnter(true)
- , containsPointer(true)
- {
- KisConfig cfg;
- disableTouchOnCanvas = cfg.disableTouchOnCanvas();
-
- moveEventCompressor.setDelay(cfg.tabletEventsDelay());
- testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents();
- testingCompressBrushEvents = cfg.testingCompressBrushEvents();
- }
-
- bool tryHidePopupPalette();
- void saveTabletEvent(const QTabletEvent *event);
- void resetSavedTabletEvent(QEvent::Type type);
- void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons);
- void addKeyShortcut(KisAbstractInputAction* action, int index,const QList<Qt::Key> &keys);
- void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture );
- void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction);
- bool processUnhandledEvent(QEvent *event);
- Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent);
- void setupActions();
- void saveTouchEvent( QTouchEvent* event );
- bool handleKisTabletEvent(QObject *object, KisTabletEvent *tevent);
-
- KisInputManager *q;
-
- KisCanvas2 *canvas;
- KisToolProxy *toolProxy;
-
- bool forwardAllEventsToTool;
- bool ignoreQtCursorEvents;
-
- bool disableTouchOnCanvas;
- bool touchHasBlockedPressEvents;
-
- KisShortcutMatcher matcher;
-#ifdef HAVE_X11
- QPointF hiResEventsWorkaroundCoeff;
-#endif
- QTabletEvent *lastTabletEvent;
- QTouchEvent *lastTouchEvent;
-
- KisToolInvocationAction *defaultInputAction;
-
- QObject *eventsReceiver;
- KisSignalCompressor moveEventCompressor;
- QScopedPointer<KisTabletEvent> compressedMoveEvent;
- bool testingAcceptCompressedTabletEvents;
- bool testingCompressBrushEvents;
-
-
- QSet<QPointer<QObject> > priorityEventFilter;
-
- template <class Event, bool useBlocking>
- void debugEvent(QEvent *event);
-
- class ProximityNotifier;
-
- class CanvasSwitcher;
- QPointer<CanvasSwitcher> canvasSwitcher;
-
- bool focusOnEnter;
- bool containsPointer;
-};
-
-template <class Event, bool useBlocking>
-void KisInputManager::Private::debugEvent(QEvent *event)
-{
- if (!KisTabletDebugger::instance()->debugEnabled()) return;
- QString msg1 = useBlocking && ignoreQtCursorEvents ? "[BLOCKED] " : "[ ]";
- Event *specificEvent = static_cast<Event*>(event);
- qDebug() << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1);
-}
-
-#define start_ignore_cursor_events() d->ignoreQtCursorEvents = true
-#define stop_ignore_cursor_events() d->ignoreQtCursorEvents = false
-#define break_if_should_ignore_cursor_events() if (d->ignoreQtCursorEvents) break;
-
-#define push_and_stop_ignore_cursor_events() bool __saved_ignore_events = d->ignoreQtCursorEvents; d->ignoreQtCursorEvents = false
-#define pop_ignore_cursor_events() d->ignoreQtCursorEvents = __saved_ignore_events
-
-#define touch_start_block_press_events() d->touchHasBlockedPressEvents = d->disableTouchOnCanvas
-#define touch_stop_block_press_events() d->touchHasBlockedPressEvents = false
-#define break_if_touch_blocked_press_events() if (d->touchHasBlockedPressEvents) break;
-
-class KisInputManager::Private::CanvasSwitcher : public QObject {
-public:
- CanvasSwitcher(Private *_d, QObject *p)
- : QObject(p),
- d(_d),
- eatOneMouseStroke(false)
- {
- }
-
- void addCanvas(KisCanvas2 *canvas) {
- QObject *widget = canvas->canvasWidget();
-
- if (!canvasResolver.contains(widget)) {
- canvasResolver.insert(widget, canvas);
- d->q->setupAsEventFilter(widget);
- widget->installEventFilter(this);
-
- d->canvas = canvas;
- d->toolProxy = dynamic_cast<KisToolProxy*>(canvas->toolProxy());
- } else {
- KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas);
- }
- }
-
- void removeCanvas(KisCanvas2 *canvas) {
- QObject *widget = canvas->canvasWidget();
-
- canvasResolver.remove(widget);
-
- if (d->eventsReceiver == widget) {
- d->q->setupAsEventFilter(0);
- }
-
- widget->removeEventFilter(this);
- }
-
- bool eventFilter(QObject* object, QEvent* event ) {
- if (canvasResolver.contains(object)) {
- switch (event->type()) {
- case QEvent::FocusIn: {
- QFocusEvent *fevent = static_cast<QFocusEvent*>(event);
- eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason);
-
- KisCanvas2 *canvas = canvasResolver.value(object);
- d->canvas = canvas;
- d->toolProxy = dynamic_cast<KisToolProxy*>(canvas->toolProxy());
-
- d->q->setupAsEventFilter(object);
-
- object->removeEventFilter(this);
- object->installEventFilter(this);
-
- QEvent event(QEvent::Enter);
- d->q->eventFilter(object, &event);
- break;
- }
-
- case QEvent::Wheel: {
- QWidget *widget = static_cast<QWidget*>(object);
- widget->setFocus();
- break;
- }
- case QEvent::MouseButtonPress:
- case QEvent::MouseButtonRelease:
- case QEvent::TabletPress:
- case QEvent::TabletRelease:
- if (eatOneMouseStroke) {
- eatOneMouseStroke--;
- return true;
- }
- break;
- case QEvent::MouseButtonDblClick:
- if (eatOneMouseStroke) {
- return true;
- }
- break;
- default:
- break;
- }
- }
- return QObject::eventFilter(object, event);
- }
-
-private:
- KisInputManager::Private *d;
- QMap<QObject*, KisCanvas2*> canvasResolver;
- int eatOneMouseStroke;
-};
-
-class KisInputManager::Private::ProximityNotifier : public QObject {
-public:
- ProximityNotifier(Private *_d, QObject *p) : QObject(p), d(_d) {}
-
- bool eventFilter(QObject* object, QEvent* event ) {
- switch (event->type()) {
- case QEvent::TabletEnterProximity:
- d->debugEvent<QEvent, false>(event);
- start_ignore_cursor_events();
- break;
- case QEvent::TabletLeaveProximity:
- d->debugEvent<QEvent, false>(event);
- stop_ignore_cursor_events();
- break;
- default:
- break;
- }
- return QObject::eventFilter(object, event);
- }
-
-private:
- KisInputManager::Private *d;
-};
-
-void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index,
- const QList<Qt::Key> &modifiers,
- Qt::MouseButtons buttons)
-{
- KisStrokeShortcut *strokeShortcut =
- new KisStrokeShortcut(action, index);
-
- QList<Qt::MouseButton> buttonList;
- if(buttons & Qt::LeftButton) {
- buttonList << Qt::LeftButton;
- }
- if(buttons & Qt::RightButton) {
- buttonList << Qt::RightButton;
- }
- if(buttons & Qt::MidButton) {
- buttonList << Qt::MidButton;
- }
- if(buttons & Qt::XButton1) {
- buttonList << Qt::XButton1;
- }
- if(buttons & Qt::XButton2) {
- buttonList << Qt::XButton2;
- }
-
- if (buttonList.size() > 0) {
- strokeShortcut->setButtons(modifiers, buttonList);
- matcher.addShortcut(strokeShortcut);
- }
-}
-
-void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index,
- const QList<Qt::Key> &keys)
-{
- if (keys.size() == 0) return;
-
- KisSingleActionShortcut *keyShortcut =
- new KisSingleActionShortcut(action, index);
-
- //Note: Ordering is important here, Shift + V is different from V + Shift,
- //which is the reason we use the last key here since most users will enter
- //shortcuts as "Shift + V". Ideally this should not happen, but this is
- //the way the shortcut matcher is currently implemented.
- QList<Qt::Key> modifiers = keys;
- Qt::Key key = modifiers.takeLast();
- keyShortcut->setKey(modifiers, key);
- matcher.addShortcut(keyShortcut);
-}
-
-void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index,
- const QList<Qt::Key> &modifiers,
- KisShortcutConfiguration::MouseWheelMovement wheelAction)
-{
- KisSingleActionShortcut *keyShortcut =
- new KisSingleActionShortcut(action, index);
-
- KisSingleActionShortcut::WheelAction a;
- switch(wheelAction) {
- case KisShortcutConfiguration::WheelUp:
- a = KisSingleActionShortcut::WheelUp;
- break;
- case KisShortcutConfiguration::WheelDown:
- a = KisSingleActionShortcut::WheelDown;
- break;
- case KisShortcutConfiguration::WheelLeft:
- a = KisSingleActionShortcut::WheelLeft;
- break;
- case KisShortcutConfiguration::WheelRight:
- a = KisSingleActionShortcut::WheelRight;
- break;
- default:
- return;
- }
-
- keyShortcut->setWheel(modifiers, a);
- matcher.addShortcut(keyShortcut);
-}
-
-void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture)
-{
- KisTouchShortcut *shortcut = new KisTouchShortcut(action, index);
- switch(gesture) {
- case KisShortcutConfiguration::PinchGesture:
- shortcut->setMinimumTouchPoints(2);
- shortcut->setMaximumTouchPoints(2);
- break;
- case KisShortcutConfiguration::PanGesture:
- shortcut->setMinimumTouchPoints(3);
- shortcut->setMaximumTouchPoints(10);
- break;
- default:
- break;
- }
- matcher.addShortcut(shortcut);
-}
-
-void KisInputManager::Private::setupActions()
-{
- QList<KisAbstractInputAction*> actions = KisInputProfileManager::instance()->actions();
- foreach(KisAbstractInputAction *action, actions) {
- KisToolInvocationAction *toolAction =
- dynamic_cast<KisToolInvocationAction*>(action);
-
- if(toolAction) {
- defaultInputAction = toolAction;
- }
- }
-
- connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged()));
- if(KisInputProfileManager::instance()->currentProfile()) {
- q->profileChanged();
- }
-}
-
-bool KisInputManager::Private::processUnhandledEvent(QEvent *event)
-{
- bool retval = false;
-
- if (forwardAllEventsToTool ||
- event->type() == QEvent::KeyPress ||
- event->type() == QEvent::KeyRelease) {
- defaultInputAction->processUnhandledEvent(event);
- retval = true;
- }
+#define start_ignore_cursor_events() d->blockMouseEvents()
+#define stop_ignore_cursor_events() d->allowMouseEvents()
- return retval && !forwardAllEventsToTool;
-}
-
-Qt::Key KisInputManager::Private::workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
-{
- Qt::Key key = (Qt::Key)keyEvent->key();
-
- if (keyEvent->key() == Qt::Key_Meta &&
- keyEvent->modifiers().testFlag(Qt::ShiftModifier)) {
-
- key = Qt::Key_Alt;
- }
-
- return key;
-}
-
-bool KisInputManager::Private::tryHidePopupPalette()
-{
- if (canvas->isPopupPaletteVisible()) {
- canvas->slotShowPopupPalette();
- return true;
- }
- return false;
-}
-
-#ifdef HAVE_X11
-inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) {
- return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y());
-}
-
-inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) {
- return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y());
-}
-#endif
-
-void KisInputManager::Private::saveTabletEvent(const QTabletEvent *event)
-{
- delete lastTabletEvent;
-
-#ifdef HAVE_X11
- /**
- * There is a bug in Qt-x11 when working in 2 tablets + 2 monitors
- * setup. The hiResGlobalPos() value gets scaled wrongly somehow.
- * Happily, the error is linear (without the offset) so we can simply
- * scale it a bit.
- */
- if (event->type() == QEvent::TabletPress) {
- if ((event->globalPos() - event->hiResGlobalPos()).manhattanLength() > 4) {
- hiResEventsWorkaroundCoeff = dividePoints(event->globalPos(), event->hiResGlobalPos());
- } else {
- hiResEventsWorkaroundCoeff = QPointF(1.0, 1.0);
- }
- }
-#endif
-
- lastTabletEvent =
- new QTabletEvent(event->type(),
- event->pos(),
- event->globalPos(),
-// QT5TODO: Qt5's QTabletEvent no longer has a hiResGlobalPos, and we don't know whether this bug still happens in Qt5.
-// #ifdef HAVE_X11
-// multiplyPoints(event->hiResGlobalPos(), hiResEventsWorkaroundCoeff),
-// #else
-// event->hiResGlobalPos(),
-// #endif
- event->device(),
- event->pointerType(),
- event->pressure(),
- event->xTilt(),
- event->yTilt(),
- event->tangentialPressure(),
- event->rotation(),
- event->z(),
- event->modifiers(),
- event->uniqueId());
-}
-
-void KisInputManager::Private::saveTouchEvent( QTouchEvent* event )
-{
- delete lastTouchEvent;
- lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints());
-}
-
-void KisInputManager::Private::resetSavedTabletEvent(QEvent::Type /*type*/)
-{
- /**
- * On both Windows and Linux each mouse event corresponds to a
- * single tablet event, so the saved event must be reset after
- * every mouse-related event
- */
-
- delete lastTabletEvent;
- lastTabletEvent = 0;
-}
+// Note: this is placeholder!
+#define touch_stop_block_press_events() //d->blockMouseEvents()
+#define touch_start_block_press_events() //d->blockMouseEvents()
+#define break_if_touch_blocked_press_events() // if (d->touchHasBlockedPressEvents) break;
KisInputManager::KisInputManager(QObject *parent)
: QObject(parent), d(new Private(this))
{
- d->canvas = 0;
- d->toolProxy = 0;
-
- d->setupActions();
-
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
SLOT(slotToolChanged()));
connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent()));
#ifndef Q_OS_MAC
QApplication::instance()->
installEventFilter(new Private::ProximityNotifier(d, this));
#endif
-
- d->canvasSwitcher = new Private::CanvasSwitcher(d, this);
}
KisInputManager::~KisInputManager()
{
delete d;
}
void KisInputManager::addTrackedCanvas(KisCanvas2 *canvas)
{
d->canvasSwitcher->addCanvas(canvas);
}
void KisInputManager::removeTrackedCanvas(KisCanvas2 *canvas)
{
d->canvasSwitcher->removeCanvas(canvas);
}
void KisInputManager::toggleTabletLogger()
{
KisTabletDebugger::instance()->toggleDebugging();
-
- bool enabled = KisTabletDebugger::instance()->debugEnabled();
- QMessageBox::information(0, i18nc("@title:window", "Krita"), enabled ?
- i18n("Tablet Event Logging Enabled") :
- i18n("Tablet Event Logging Disabled"));
- if (enabled) {
- qDebug() << "vvvvvvvvvvvvvvvvvvvvvvv START TABLET EVENT LOG vvvvvvvvvvvvvvvvvvvvvvv";
- }
- else {
- qDebug() << "^^^^^^^^^^^^^^^^^^^^^^^ START TABLET EVENT LOG ^^^^^^^^^^^^^^^^^^^^^^^";
- }
}
void KisInputManager::attachPriorityEventFilter(QObject *filter)
{
d->priorityEventFilter.insert(QPointer<QObject>(filter));
}
void KisInputManager::detachPriorityEventFilter(QObject *filter)
{
d->priorityEventFilter.remove(QPointer<QObject>(filter));
}
void KisInputManager::setupAsEventFilter(QObject *receiver)
{
if (d->eventsReceiver) {
d->eventsReceiver->removeEventFilter(this);
}
d->eventsReceiver = receiver;
if (d->eventsReceiver) {
d->eventsReceiver->installEventFilter(this);
}
}
void KisInputManager::stopIgnoringEvents()
{
stop_ignore_cursor_events();
}
void KisInputManager::slotFocusOnEnter(bool value)
{
if (d->focusOnEnter == value) {
return;
}
d->focusOnEnter = value;
if (d->focusOnEnter && d->containsPointer) {
if (d->canvas) {
d->canvas->canvasWidget()->setFocus();
}
}
}
#if defined (__clang__)
#pragma GCC diagnostic ignored "-Wswitch"
#endif
bool KisInputManager::eventFilter(QObject* object, QEvent* event)
{
bool retval = false;
if (object != d->eventsReceiver) return retval;
- if (!d->ignoreQtCursorEvents ||
- (event->type() != QEvent::MouseButtonPress &&
- event->type() != QEvent::MouseButtonDblClick &&
- event->type() != QEvent::MouseButtonRelease &&
- event->type() != QEvent::MouseMove &&
- event->type() != QEvent::TabletPress &&
- event->type() != QEvent::TabletMove &&
- event->type() != QEvent::TabletRelease)) {
+ if (true) {
foreach (QPointer<QObject> filter, d->priorityEventFilter) {
if (filter.isNull()) {
d->priorityEventFilter.remove(filter);
continue;
}
if (filter->eventFilter(object, event)) return true;
}
}
// KoToolProxy needs to pre-process some events to ensure the
// global shortcuts (not the input manager's ones) are not
// executed, in particular, this line will accept events when the
// tool is in text editing, preventing shortcut triggering
d->toolProxy->processEvent(event);
- // because we have fake enums in here...
+ // TODO: Handle touch events correctly.
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick: {
d->debugEvent<QMouseEvent, true>(event);
- break_if_should_ignore_cursor_events();
- break_if_touch_blocked_press_events();
+ // break_if_touch_blocked_press_events();
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (d->tryHidePopupPalette()) {
retval = true;
} else {
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent);
}
- d->resetSavedTabletEvent(event->type());
event->setAccepted(retval);
break;
}
case QEvent::MouseButtonRelease: {
d->debugEvent<QMouseEvent, true>(event);
- break_if_should_ignore_cursor_events();
- break_if_touch_blocked_press_events();
+ // break_if_touch_blocked_press_events();
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
- d->resetSavedTabletEvent(event->type());
event->setAccepted(retval);
break;
}
case QEvent::ShortcutOverride: {
d->debugEvent<QKeyEvent, false>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
Qt::Key key = d->workaroundShiftAltMetaHell(keyEvent);
if (!keyEvent->isAutoRepeat()) {
retval = d->matcher.keyPressed(key);
} else {
retval = d->matcher.autoRepeatedKeyPressed(key);
}
/**
* Workaround for temporary switching of tools by
* KoCanvasControllerWidget. We don't need this switch because
* we handle it ourselves.
*/
retval |= !d->forwardAllEventsToTool &&
(keyEvent->key() == Qt::Key_Space ||
keyEvent->key() == Qt::Key_Escape);
break;
}
case QEvent::KeyRelease: {
d->debugEvent<QKeyEvent, false>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (!keyEvent->isAutoRepeat()) {
Qt::Key key = d->workaroundShiftAltMetaHell(keyEvent);
retval = d->matcher.keyReleased(key);
}
break;
}
case QEvent::MouseMove: {
d->debugEvent<QMouseEvent, true>(event);
- break_if_should_ignore_cursor_events();
- QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
- if (!d->matcher.mouseMoved(mouseEvent)) {
+ if (!d->matcher.pointerMoved(event)) {
//Update the current tool so things like the brush outline gets updated.
- d->toolProxy->forwardMouseHoverEvent(mouseEvent, lastTabletEvent());
+ d->toolProxy->forwardHoverEvent(event);
}
retval = true;
event->setAccepted(retval);
- d->resetSavedTabletEvent(event->type());
break;
}
case QEvent::Wheel: {
d->debugEvent<QWheelEvent, false>(event);
QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
KisSingleActionShortcut::WheelAction action;
if(wheelEvent->orientation() == Qt::Horizontal) {
if(wheelEvent->delta() < 0) {
action = KisSingleActionShortcut::WheelRight;
}
else {
action = KisSingleActionShortcut::WheelLeft;
}
}
else {
if(wheelEvent->delta() > 0) {
action = KisSingleActionShortcut::WheelUp;
}
else {
action = KisSingleActionShortcut::WheelDown;
}
}
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.wheelEvent(action, wheelEvent);
break;
}
case QEvent::Enter:
d->debugEvent<QEvent, false>(event);
d->containsPointer = true;
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
//Ensure we have focus so we get key events.
if (d->focusOnEnter) {
d->canvas->canvasWidget()->setFocus();
}
stop_ignore_cursor_events();
touch_stop_block_press_events();
d->matcher.enterEvent();
break;
case QEvent::Leave:
d->debugEvent<QEvent, false>(event);
d->containsPointer = false;
/**
* We won't get a TabletProximityLeave event when the tablet
* is hovering above some other widget, so restore cursor
* events processing right now.
*/
stop_ignore_cursor_events();
touch_stop_block_press_events();
d->matcher.leaveEvent();
break;
case QEvent::FocusIn:
d->debugEvent<QEvent, false>(event);
KisAbstractInputAction::setInputManager(this);
//Clear all state so we don't have half-matched shortcuts dangling around.
d->matcher.reinitialize();
{ // Emulate pressing of the key that are already pressed
KisExtendedModifiersMapper mapper;
Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
foreach (Qt::Key key, mapper.queryExtendedModifiers()) {
QKeyEvent kevent(QEvent::KeyPress, key, modifiers);
eventFilter(object, &kevent);
}
}
stop_ignore_cursor_events();
break;
- case QEvent::TabletPress:
- case QEvent::TabletMove:
case QEvent::TabletRelease: {
- d->debugEvent<QTabletEvent, true>(event);
- break_if_should_ignore_cursor_events();
- break_if_touch_blocked_press_events();
-
- //We want both the tablet information and the mouse button state.
- //Since QTabletEvent only provides the tablet information, we
- //save that and then ignore the event so it will generate a mouse
- //event.
- QTabletEvent* tabletEvent = static_cast<QTabletEvent*>(event);
- d->saveTabletEvent(tabletEvent);
- event->ignore();
+ // break_if_touch_blocked_press_events();
+ d->debugEvent<QTabletEvent, false>(event);
+ QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
+ retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent);
+ retval = true;
+ event->setAccepted(true);
+ stop_ignore_cursor_events();
break;
}
- case KisTabletEvent::TabletPressEx:
- case KisTabletEvent::TabletMoveEx:
- case KisTabletEvent::TabletReleaseEx: {
- d->debugEvent<KisTabletEvent, false>(event);
- stop_ignore_cursor_events();
- touch_stop_block_press_events();
-
- KisTabletEvent *tevent = static_cast<KisTabletEvent*>(event);
- if (tevent->type() == (QEvent::Type)KisTabletEvent::TabletMoveEx &&
- (!d->matcher.supportsHiResInputEvents() ||
- d->testingCompressBrushEvents)) {
- d->compressedMoveEvent.reset(new KisTabletEvent(*tevent));
- d->moveEventCompressor.start();
+ case QEvent::TabletMove: {
+ d->debugEvent<QTabletEvent, false>(event);
+ QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
- /**
- * On Linux Qt eats the rest of unneeded events if we
- * ignore the first of the chunk of tablet events. So
- * generally we should never activate this feature. Only
- * for testing purposes!
- */
- if (d->testingAcceptCompressedTabletEvents) {
- tevent->setAccepted(true);
- }
-
- retval = true;
- } else {
- slotCompressedMoveEvent();
- retval = d->handleKisTabletEvent(object, tevent);
+ if (!d->matcher.pointerMoved(tabletEvent)) {
+ d->toolProxy->forwardHoverEvent(tabletEvent);
}
+ retval = true;
+ event->setAccepted(true);
/**
* The flow of tablet events means the tablet is in the
* proximity area, so activate it even when the
* TabletEnterProximity event was missed (may happen when
* changing focus of the window with tablet in the proximity
* area)
*/
start_ignore_cursor_events();
-
break;
}
- case KisTabletEvent::TouchProximityInEx: {
- touch_start_block_press_events();
- break;
- }
- case KisTabletEvent::TouchProximityOutEx: {
- touch_stop_block_press_events();
+
+ case QEvent::TabletPress: {
+ d->debugEvent<QTabletEvent, true>(event);
+ QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
+ if (d->tryHidePopupPalette()) {
+ retval = true;
+ } else {
+ //Make sure the input actions know we are active.
+ KisAbstractInputAction::setInputManager(this);
+ retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent);
+ }
+ event->setAccepted(true);
+ retval = true;
+ start_ignore_cursor_events();
break;
}
case QEvent::TouchBegin:
touch_start_block_press_events();
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
- d->resetSavedTabletEvent(event->type());
+ // d->resetSavedTabletEvent(event->type());
break;
case QEvent::TouchUpdate:
touch_start_block_press_events();
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchUpdateEvent(static_cast<QTouchEvent*>(event));
event->accept();
- d->resetSavedTabletEvent(event->type());
+ // d->resetSavedTabletEvent(event->type());
break;
case QEvent::TouchEnd:
touch_stop_block_press_events();
d->saveTouchEvent(static_cast<QTouchEvent*>(event));
retval = d->matcher.touchEndEvent(static_cast<QTouchEvent*>(event));
event->accept();
- d->resetSavedTabletEvent(event->type());
+ // d->resetSavedTabletEvent(event->type());
delete d->lastTouchEvent;
d->lastTouchEvent = 0;
break;
default:
break;
}
return !retval ? d->processUnhandledEvent(event) : true;
}
-bool KisInputManager::Private::handleKisTabletEvent(QObject *object, KisTabletEvent *tevent)
-{
- if(object == 0) return false;
-
- bool retval = false;
-
- QTabletEvent qte = tevent->toQTabletEvent();
- qte.ignore();
- retval = q->eventFilter(object, &qte);
- tevent->setAccepted(qte.isAccepted());
-
- if (!retval && !qte.isAccepted()) {
- QMouseEvent qme = tevent->toQMouseEvent();
- qme.ignore();
- retval = q->eventFilter(object, &qme);
- tevent->setAccepted(qme.isAccepted());
- }
-
- return retval;
-}
void KisInputManager::slotCompressedMoveEvent()
{
if (d->compressedMoveEvent) {
- push_and_stop_ignore_cursor_events();
- touch_stop_block_press_events();
-
- (void) d->handleKisTabletEvent(d->eventsReceiver, d->compressedMoveEvent.data());
+ // touch_stop_block_press_events();
- pop_ignore_cursor_events();
+ (void) d->handleCompressedTabletEvent(d->eventsReceiver, d->compressedMoveEvent.data());
d->compressedMoveEvent.reset();
+ qDebug() << "Compressed move event received.";
}
}
KisCanvas2* KisInputManager::canvas() const
{
return d->canvas;
}
KisToolProxy* KisInputManager::toolProxy() const
{
return d->toolProxy;
}
-QTabletEvent* KisInputManager::lastTabletEvent() const
-{
- return d->lastTabletEvent;
-}
-
QTouchEvent *KisInputManager::lastTouchEvent() const
{
return d->lastTouchEvent;
}
void KisInputManager::slotToolChanged()
{
QString toolId = KoToolManager::instance()->activeToolId();
if (toolId == "ArtisticTextToolFactoryID" || toolId == "TextToolFactory_ID") {
d->forwardAllEventsToTool = true;
d->matcher.suppressAllActions(true);
} else {
d->forwardAllEventsToTool = false;
d->matcher.suppressAllActions(false);
}
}
QPointF KisInputManager::widgetToDocument(const QPointF& position)
{
QPointF pixel = QPointF(position.x() + 0.5f, position.y() + 0.5f);
return d->canvas->coordinatesConverter()->widgetToDocument(pixel);
}
void KisInputManager::profileChanged()
{
d->matcher.clearShortcuts();
KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile();
if (profile) {
QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();
foreach(KisShortcutConfiguration *shortcut, shortcuts) {
switch(shortcut->type()) {
case KisShortcutConfiguration::KeyCombinationType:
d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys());
break;
case KisShortcutConfiguration::MouseButtonType:
d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons());
break;
case KisShortcutConfiguration::MouseWheelType:
d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
break;
case KisShortcutConfiguration::GestureType:
d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
break;
default:
break;
}
}
}
else {
- kWarning() << "No Input Profile Found: canvas interaction will be impossible";
+ dbgKrita << "No Input Profile Found: canvas interaction will be impossible";
}
}
diff --git a/krita/ui/input/kis_input_manager.h b/krita/ui/input/kis_input_manager.h
index 4c2b5e24633..4df880bb48a 100644
--- a/krita/ui/input/kis_input_manager.h
+++ b/krita/ui/input/kis_input_manager.h
@@ -1,131 +1,125 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_INPUTMANAGER_H
#define KIS_INPUTMANAGER_H
#include <QObject>
#include <kritaui_export.h>
class QPointF;
class QTabletEvent;
class QTouchEvent;
class KisToolProxy;
class KisCanvas2;
/**
* \brief Central object to manage canvas input.
*
* The Input Manager class manages all canvas input. It is created
* by KisCanvas2 and processes all events related to input sent to the
* canvas.
*
* The Input Manager keeps track of a set of actions and a set of
* shortcuts. The actions are pre-defined while the shortcuts are
* set from configuration.
*
* For each event, it will try to determine if there is a shortcut that
* matches the input. It will then activate this action and pass all
* consecutive events on to this action.
*
* \sa KisAbstractInputAction
*
* \todo Implement shortcut configuration
*/
class KRITAUI_EXPORT KisInputManager : public QObject
{
Q_OBJECT
public:
/**
* Constructor.
*/
KisInputManager(QObject *parent);
/**
* Destructor.
*/
~KisInputManager();
void addTrackedCanvas(KisCanvas2 *canvas);
void removeTrackedCanvas(KisCanvas2 *canvas);
void toggleTabletLogger();
/**
* Installs the input manager as an event filter for \p receiver.
* Please note that KisInputManager is supposed to handle events
* for a single receiver only. This is defined by the fact that it
* resends some of the events back through the Qt's queue to the
* reciever. That is why the input manager will assert when it gets
* an event with wrong destination.
*/
void setupAsEventFilter(QObject *receiver);
/**
* Event filter method. Overridden from QObject.
*/
bool eventFilter(QObject* object, QEvent* event );
void attachPriorityEventFilter(QObject *filter);
void detachPriorityEventFilter(QObject *filter);
/**
* Return the canvas this input manager is associated with.
*/
KisCanvas2 *canvas() const;
/**
* The tool proxy of the current application.
*/
KisToolProxy *toolProxy() const;
- /**
- * Returns the event object for the last tablet event
- * happened. Returns null if there was no tablet event recently
- */
- QTabletEvent *lastTabletEvent() const;
-
/**
* Touch events are special, too.
*
* \return a touch event if there was one, otherwise 0
*/
QTouchEvent *lastTouchEvent() const;
/**
* Convert a widget position to a document position.
*/
QPointF widgetToDocument(const QPointF &position);
public Q_SLOTS:
void stopIgnoringEvents();
void slotFocusOnEnter(bool value);
private Q_SLOTS:
void slotToolChanged();
void profileChanged();
void slotCompressedMoveEvent();
private:
class Private;
Private* const d;
};
#endif // KIS_INPUTMANAGER_H
diff --git a/krita/ui/input/kis_input_manager_p.cpp b/krita/ui/input/kis_input_manager_p.cpp
new file mode 100644
index 00000000000..814cf2fe8d2
--- /dev/null
+++ b/krita/ui/input/kis_input_manager_p.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_input_manager_p.h"
+
+#include <QMap>
+#include <QApplication>
+#include <QScopedPointer>
+#include <QtGlobal>
+
+#include "kis_config.h"
+#include "kis_abstract_input_action.h"
+#include "kis_stroke_shortcut.h"
+#include "kis_touch_shortcut.h"
+#include "kis_input_profile_manager.h"
+#include "input/kis_tablet_debugger.h"
+
+
+
+// Note: this class is intended to model all event masking logic.
+class EventEater : public QObject
+{
+public:
+ EventEater() : QObject(qApp), hungry(false) {}
+
+ bool eventFilter(QObject* object, QEvent* event )
+ {
+ if (hungry && (event->type() == QEvent::MouseMove ||
+ event->type() == QEvent::MouseButtonPress ||
+ event->type() == QEvent::MouseButtonRelease)) {
+ if (KisTabletDebugger::instance()->debugEnabled()) {
+ QString pre = QString("[FILTERED]");
+ QMouseEvent *ev = static_cast<QMouseEvent*>(event);
+ dbgTablet << KisTabletDebugger::instance()->eventToString(*ev,pre);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void activate()
+ {
+ if (!hungry && (KisTabletDebugger::instance()->debugEnabled()))
+ dbgTablet << "Ignoring mouse events.";
+ hungry = true;
+ }
+
+ void deactivate()
+ {
+ if (!hungry && (KisTabletDebugger::instance()->debugEnabled()))
+ dbgTablet << "Accepting mouse events.";
+ hungry = false;
+ }
+
+ bool isActive()
+ {
+ return hungry;
+ }
+
+private:
+ bool hungry;
+};
+
+// Should be Q_GLOBAL_STATIC?
+EventEater *globalEventEater;
+
+bool KisInputManager::Private::ignoreQtCursorEvents()
+{
+ return globalEventEater->isActive();
+}
+
+KisInputManager::Private::Private(KisInputManager *qq)
+ : q(qq)
+ , canvas(0)
+ , toolProxy(0)
+ , forwardAllEventsToTool(false)
+ , disableTouchOnCanvas(false)
+ , touchHasBlockedPressEvents(false)
+ , lastTouchEvent(0)
+ , defaultInputAction(0)
+ , eventsReceiver(0)
+ , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE)
+ , testingAcceptCompressedTabletEvents(false)
+ , testingCompressBrushEvents(false)
+ , focusOnEnter(true)
+ , containsPointer(true)
+{
+ KisConfig cfg;
+ disableTouchOnCanvas = cfg.disableTouchOnCanvas();
+
+ moveEventCompressor.setDelay(cfg.tabletEventsDelay());
+ testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents();
+ testingCompressBrushEvents = cfg.testingCompressBrushEvents();
+ setupActions();
+ canvasSwitcher = new CanvasSwitcher(this, q);
+ globalEventEater = new EventEater();
+ qApp->installEventFilter(globalEventEater);
+}
+
+
+
+KisInputManager::Private::~Private()
+{
+ delete canvasSwitcher;
+ delete globalEventEater;
+}
+
+KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p)
+ : QObject(p),
+ d(_d),
+ eatOneMouseStroke(false)
+{
+}
+
+void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas)
+{
+ QObject *widget = canvas->canvasWidget();
+
+ if (!canvasResolver.contains(widget)) {
+ canvasResolver.insert(widget, canvas);
+ d->q->setupAsEventFilter(widget);
+ widget->installEventFilter(this);
+
+ d->canvas = canvas;
+ d->toolProxy = dynamic_cast<KisToolProxy*>(canvas->toolProxy());
+ } else {
+ KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas);
+ }
+}
+
+void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas)
+{
+ QObject *widget = canvas->canvasWidget();
+
+ canvasResolver.remove(widget);
+
+ if (d->eventsReceiver == widget) {
+ d->q->setupAsEventFilter(0);
+ }
+
+ widget->removeEventFilter(this);
+}
+
+bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event )
+{
+ if (canvasResolver.contains(object)) {
+ switch (event->type()) {
+ case QEvent::FocusIn: {
+ QFocusEvent *fevent = static_cast<QFocusEvent*>(event);
+ eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason);
+
+ KisCanvas2 *canvas = canvasResolver.value(object);
+ d->canvas = canvas;
+ d->toolProxy = dynamic_cast<KisToolProxy*>(canvas->toolProxy());
+
+ d->q->setupAsEventFilter(object);
+
+ object->removeEventFilter(this);
+ object->installEventFilter(this);
+
+ QEvent event(QEvent::Enter);
+ d->q->eventFilter(object, &event);
+ break;
+ }
+
+ case QEvent::Wheel: {
+ QWidget *widget = static_cast<QWidget*>(object);
+ widget->setFocus();
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::TabletPress:
+ case QEvent::TabletRelease:
+ if (eatOneMouseStroke) {
+ eatOneMouseStroke--;
+ return true;
+ }
+ break;
+ case QEvent::MouseButtonDblClick:
+ if (eatOneMouseStroke) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QObject::eventFilter(object, event);
+}
+
+KisInputManager::Private::ProximityNotifier::ProximityNotifier(KisInputManager::Private *_d, QObject *p)
+ : QObject(p), d(_d)
+{}
+
+bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event )
+{
+ switch (event->type()) {
+ case QEvent::TabletEnterProximity:
+ d->debugEvent<QEvent, false>(event);
+ // d->blockMouseEvents(); Qt sends fake mouse events instead of hover events, so disable this for now.
+ break;
+ case QEvent::TabletLeaveProximity:
+ d->debugEvent<QEvent, false>(event);
+ d->allowMouseEvents();
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(object, event);
+}
+
+void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index,
+ const QList<Qt::Key> &modifiers,
+ Qt::MouseButtons buttons)
+{
+ KisStrokeShortcut *strokeShortcut =
+ new KisStrokeShortcut(action, index);
+
+ QList<Qt::MouseButton> buttonList;
+ if(buttons & Qt::LeftButton) {
+ buttonList << Qt::LeftButton;
+ }
+ if(buttons & Qt::RightButton) {
+ buttonList << Qt::RightButton;
+ }
+ if(buttons & Qt::MidButton) {
+ buttonList << Qt::MidButton;
+ }
+ if(buttons & Qt::XButton1) {
+ buttonList << Qt::XButton1;
+ }
+ if(buttons & Qt::XButton2) {
+ buttonList << Qt::XButton2;
+ }
+
+ if (buttonList.size() > 0) {
+ strokeShortcut->setButtons(modifiers, buttonList);
+ matcher.addShortcut(strokeShortcut);
+ }
+}
+
+void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index,
+ const QList<Qt::Key> &keys)
+{
+ if (keys.size() == 0) return;
+
+ KisSingleActionShortcut *keyShortcut =
+ new KisSingleActionShortcut(action, index);
+
+ //Note: Ordering is important here, Shift + V is different from V + Shift,
+ //which is the reason we use the last key here since most users will enter
+ //shortcuts as "Shift + V". Ideally this should not happen, but this is
+ //the way the shortcut matcher is currently implemented.
+ QList<Qt::Key> modifiers = keys;
+ Qt::Key key = modifiers.takeLast();
+ keyShortcut->setKey(modifiers, key);
+ matcher.addShortcut(keyShortcut);
+}
+
+void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index,
+ const QList<Qt::Key> &modifiers,
+ KisShortcutConfiguration::MouseWheelMovement wheelAction)
+{
+ KisSingleActionShortcut *keyShortcut =
+ new KisSingleActionShortcut(action, index);
+
+ KisSingleActionShortcut::WheelAction a;
+ switch(wheelAction) {
+ case KisShortcutConfiguration::WheelUp:
+ a = KisSingleActionShortcut::WheelUp;
+ break;
+ case KisShortcutConfiguration::WheelDown:
+ a = KisSingleActionShortcut::WheelDown;
+ break;
+ case KisShortcutConfiguration::WheelLeft:
+ a = KisSingleActionShortcut::WheelLeft;
+ break;
+ case KisShortcutConfiguration::WheelRight:
+ a = KisSingleActionShortcut::WheelRight;
+ break;
+ default:
+ return;
+ }
+
+ keyShortcut->setWheel(modifiers, a);
+ matcher.addShortcut(keyShortcut);
+}
+
+void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture)
+{
+ KisTouchShortcut *shortcut = new KisTouchShortcut(action, index);
+ switch(gesture) {
+ case KisShortcutConfiguration::PinchGesture:
+ shortcut->setMinimumTouchPoints(2);
+ shortcut->setMaximumTouchPoints(2);
+ break;
+ case KisShortcutConfiguration::PanGesture:
+ shortcut->setMinimumTouchPoints(3);
+ shortcut->setMaximumTouchPoints(10);
+ break;
+ default:
+ break;
+ }
+ matcher.addShortcut(shortcut);
+}
+
+void KisInputManager::Private::setupActions()
+{
+ QList<KisAbstractInputAction*> actions = KisInputProfileManager::instance()->actions();
+ foreach(KisAbstractInputAction *action, actions) {
+ KisToolInvocationAction *toolAction =
+ dynamic_cast<KisToolInvocationAction*>(action);
+
+ if(toolAction) {
+ defaultInputAction = toolAction;
+ }
+ }
+
+ connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged()));
+ if(KisInputProfileManager::instance()->currentProfile()) {
+ q->profileChanged();
+ }
+}
+
+bool KisInputManager::Private::processUnhandledEvent(QEvent *event)
+{
+ bool retval = false;
+
+ if (forwardAllEventsToTool ||
+ event->type() == QEvent::KeyPress ||
+ event->type() == QEvent::KeyRelease) {
+
+ defaultInputAction->processUnhandledEvent(event);
+ retval = true;
+ }
+
+ return retval && !forwardAllEventsToTool;
+}
+
+Qt::Key KisInputManager::Private::workaroundShiftAltMetaHell(const QKeyEvent *keyEvent)
+{
+ Qt::Key key = (Qt::Key)keyEvent->key();
+
+ if (keyEvent->key() == Qt::Key_Meta &&
+ keyEvent->modifiers().testFlag(Qt::ShiftModifier)) {
+
+ key = Qt::Key_Alt;
+ }
+
+ return key;
+}
+
+bool KisInputManager::Private::tryHidePopupPalette()
+{
+ if (canvas->isPopupPaletteVisible()) {
+ canvas->slotShowPopupPalette();
+ return true;
+ }
+ return false;
+}
+
+#ifdef HAVE_X11
+inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) {
+ return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y());
+}
+
+inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) {
+ return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y());
+}
+#endif
+
+void KisInputManager::Private::saveTouchEvent( QTouchEvent* event )
+{
+ delete lastTouchEvent;
+ lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints());
+}
+
+
+void KisInputManager::Private::blockMouseEvents()
+{
+ globalEventEater->activate();
+}
+
+void KisInputManager::Private::allowMouseEvents()
+{
+ globalEventEater->deactivate();
+}
+
+bool KisInputManager::Private::handleCompressedTabletEvent(QObject *object, QTabletEvent *tevent)
+{
+ if(object == 0) return false;
+
+ bool retval = false;
+
+ retval = q->eventFilter(object, tevent);
+
+ if (!retval && !tevent->isAccepted()) {
+ dbgTablet << "Rejected a compressed tablet event.";
+ }
+
+ return retval;
+}
diff --git a/krita/ui/input/kis_input_manager_p.h b/krita/ui/input/kis_input_manager_p.h
new file mode 100644
index 00000000000..5c3e59860dd
--- /dev/null
+++ b/krita/ui/input/kis_input_manager_p.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <QList>
+#include <QPointer>
+#include <QSet>
+#include <QEvent>
+#include <QTouchEvent>
+#include <QScopedPointer>
+
+#include "kis_input_manager.h"
+#include "kis_shortcut_matcher.h"
+#include "kis_tool_invocation_action.h"
+#include "kis_alternate_invocation_action.h"
+#include "kis_shortcut_configuration.h"
+#include "kis_canvas2.h"
+#include "kis_tool_proxy.h"
+#include "kis_signal_compressor.h"
+#include "input/kis_tablet_debugger.h"
+
+#include "kis_abstract_input_action.h"
+
+
+class KisInputManager::Private
+{
+public:
+ Private(KisInputManager *qq);
+ ~Private();
+ bool tryHidePopupPalette();
+ void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons);
+ void addKeyShortcut(KisAbstractInputAction* action, int index,const QList<Qt::Key> &keys);
+ void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture );
+ void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction);
+ bool processUnhandledEvent(QEvent *event);
+ Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent);
+ void setupActions();
+ void saveTouchEvent( QTouchEvent* event );
+ bool handleCompressedTabletEvent(QObject *object, QTabletEvent *tevent);
+
+ KisInputManager *q;
+
+ KisCanvas2 *canvas;
+ KisToolProxy *toolProxy;
+
+ bool forwardAllEventsToTool;
+ bool ignoreQtCursorEvents();
+
+ bool disableTouchOnCanvas;
+ bool touchHasBlockedPressEvents;
+
+ KisShortcutMatcher matcher;
+ QTouchEvent *lastTouchEvent;
+
+ KisToolInvocationAction *defaultInputAction;
+
+ QObject *eventsReceiver;
+ KisSignalCompressor moveEventCompressor;
+ QScopedPointer<QTabletEvent> compressedMoveEvent;
+ bool testingAcceptCompressedTabletEvents;
+ bool testingCompressBrushEvents;
+
+
+ QSet<QPointer<QObject> > priorityEventFilter;
+
+ void blockMouseEvents();
+ void allowMouseEvents();
+
+ template <class Event, bool useBlocking>
+ void debugEvent(QEvent *event)
+ {
+ if (!KisTabletDebugger::instance()->debugEnabled()) return;
+ QString msg1 = useBlocking && ignoreQtCursorEvents() ? "[BLOCKED] " : "[ ]";
+ Event *specificEvent = static_cast<Event*>(event);
+ dbgTablet << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1);
+ }
+
+ class ProximityNotifier : public QObject
+ {
+ public:
+ ProximityNotifier(Private *_d, QObject *p);
+ bool eventFilter(QObject* object, QEvent* event );
+ private:
+ KisInputManager::Private *d;
+ };
+
+ class CanvasSwitcher : public QObject
+ {
+ public:
+ CanvasSwitcher(Private *_d, QObject *p);
+ void addCanvas(KisCanvas2 *canvas);
+ void removeCanvas(KisCanvas2 *canvas);
+ bool eventFilter(QObject* object, QEvent* event );
+
+ private:
+ KisInputManager::Private *d;
+ QMap<QObject*, KisCanvas2*> canvasResolver;
+ int eatOneMouseStroke;
+ };
+ QPointer<CanvasSwitcher> canvasSwitcher;
+
+ bool focusOnEnter;
+ bool containsPointer;
+};
diff --git a/krita/ui/input/kis_input_profile_manager.cpp b/krita/ui/input/kis_input_profile_manager.cpp
index 54f6a85537e..04105f44043 100644
--- a/krita/ui/input/kis_input_profile_manager.cpp
+++ b/krita/ui/input/kis_input_profile_manager.cpp
@@ -1,365 +1,365 @@
/*
* This file is part of the KDE project
* Copyright (C) 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_input_profile_manager.h"
#include "kis_input_profile.h"
#include <QMap>
#include <QStringList>
#include <QDir>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include "kis_config.h"
#include "kis_alternate_invocation_action.h"
#include "kis_change_primary_setting_action.h"
#include "kis_pan_action.h"
#include "kis_rotate_canvas_action.h"
#include "kis_show_palette_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_zoom_action.h"
#include "kis_shortcut_configuration.h"
#include "kis_select_layer_action.h"
#include "kis_gamma_exposure_action.h"
#define PROFILE_VERSION 3
class Q_DECL_HIDDEN KisInputProfileManager::Private
{
public:
Private() : currentProfile(0) { }
void createActions();
QString profileFileName(const QString &profileName);
KisInputProfile *currentProfile;
QMap<QString, KisInputProfile *> profiles;
QList<KisAbstractInputAction *> actions;
};
K_GLOBAL_STATIC(KisInputProfileManager, inputProfileManager)
KisInputProfileManager *KisInputProfileManager::instance()
{
return inputProfileManager;
}
QList< KisInputProfile * > KisInputProfileManager::profiles() const
{
return d->profiles.values();
}
QStringList KisInputProfileManager::profileNames() const
{
return d->profiles.keys();
}
KisInputProfile *KisInputProfileManager::profile(const QString &name) const
{
if (d->profiles.contains(name)) {
return d->profiles.value(name);
}
return 0;
}
KisInputProfile *KisInputProfileManager::currentProfile() const
{
return d->currentProfile;
}
void KisInputProfileManager::setCurrentProfile(KisInputProfile *profile)
{
if (profile && profile != d->currentProfile) {
d->currentProfile = profile;
emit currentProfileChanged();
}
}
KisInputProfile *KisInputProfileManager::addProfile(const QString &name)
{
if (d->profiles.contains(name)) {
return d->profiles.value(name);
}
KisInputProfile *profile = new KisInputProfile(this);
profile->setName(name);
d->profiles.insert(name, profile);
emit profilesChanged();
return profile;
}
void KisInputProfileManager::removeProfile(const QString &name)
{
if (d->profiles.contains(name)) {
QString currentProfileName = d->currentProfile->name();
delete d->profiles.value(name);
d->profiles.remove(name);
//Delete the settings file for the removed profile, if it exists
QDir userDir(KGlobal::dirs()->saveLocation("data", "krita/input/"));
if (userDir.exists(d->profileFileName(name))) {
userDir.remove(d->profileFileName(name));
}
if (currentProfileName == name) {
d->currentProfile = d->profiles.begin().value();
emit currentProfileChanged();
}
emit profilesChanged();
}
}
bool KisInputProfileManager::renameProfile(const QString &oldName, const QString &newName)
{
if (!d->profiles.contains(oldName)) {
return false;
}
KisInputProfile *profile = d->profiles.value(oldName);
d->profiles.remove(oldName);
profile->setName(newName);
d->profiles.insert(newName, profile);
emit profilesChanged();
return true;
}
void KisInputProfileManager::duplicateProfile(const QString &name, const QString &newName)
{
if (!d->profiles.contains(name) || d->profiles.contains(newName)) {
return;
}
KisInputProfile *newProfile = new KisInputProfile(this);
newProfile->setName(newName);
d->profiles.insert(newName, newProfile);
KisInputProfile *profile = d->profiles.value(name);
QList<KisShortcutConfiguration *> shortcuts = profile->allShortcuts();
Q_FOREACH(KisShortcutConfiguration * shortcut, shortcuts) {
newProfile->addShortcut(new KisShortcutConfiguration(*shortcut));
}
emit profilesChanged();
}
QList< KisAbstractInputAction * > KisInputProfileManager::actions()
{
return d->actions;
}
struct ProfileEntry {
QString name;
QString fullpath;
int version;
};
void KisInputProfileManager::loadProfiles()
{
//Remove any profiles that already exist
d->currentProfile = 0;
qDeleteAll(d->profiles);
d->profiles.clear();
//Look up all profiles (this includes those installed to $prefix as well as the user's local data dir)
QStringList profiles = KGlobal::dirs()->findAllResources("data", "krita/input/*", KStandardDirs::Recursive);
- qDebug() << "profiles" << profiles;
+ dbgKrita << "profiles" << profiles;
QMap<QString, QList<ProfileEntry> > profileEntries;
// Get only valid entries...
Q_FOREACH(const QString & p, profiles) {
ProfileEntry entry;
entry.fullpath = p;
KConfig config(p, KConfig::SimpleConfig);
if (!config.hasGroup("General") || !config.group("General").hasKey("name") || !config.group("General").hasKey("version")) {
//Skip if we don't have the proper settings.
continue;
}
// Only entries of exactly the right version can be considered
entry.version = config.group("General").readEntry("version", 0);
if (entry.version != PROFILE_VERSION) {
continue;
}
entry.name = config.group("General").readEntry("name");
if (!profileEntries.contains(entry.name)) {
profileEntries[entry.name] = QList<ProfileEntry>();
}
if (p.contains(".kde") || p.contains(".krita")) {
// It's the user define one, drop the others
profileEntries[entry.name].clear();
profileEntries[entry.name].append(entry);
break;
}
else {
profileEntries[entry.name].append(entry);
}
}
Q_FOREACH(const QString & profileName, profileEntries.keys()) {
if (profileEntries[profileName].isEmpty()) {
continue;
}
// we have one or more entries for this profile name. We'll take the first,
// because that's the most local one.
ProfileEntry entry = profileEntries[profileName].first();
KConfig config(entry.fullpath, KConfig::SimpleConfig);
KisInputProfile *newProfile = addProfile(entry.name);
Q_FOREACH(KisAbstractInputAction * action, d->actions) {
if (!config.hasGroup(action->id())) {
continue;
}
KConfigGroup grp = config.group(action->id());
//Read the settings for the action and create the appropriate shortcuts.
Q_FOREACH(const QString & entry, grp.entryMap()) {
KisShortcutConfiguration *shortcut = new KisShortcutConfiguration;
shortcut->setAction(action);
if (shortcut->unserialize(entry)) {
newProfile->addShortcut(shortcut);
}
else {
delete shortcut;
}
}
}
}
KisConfig cfg;
QString currentProfile = cfg.currentInputProfile();
if (d->profiles.size() > 0) {
if (currentProfile.isEmpty() || !d->profiles.contains(currentProfile)) {
d->currentProfile = d->profiles.begin().value();
}
else {
d->currentProfile = d->profiles.value(currentProfile);
}
}
if (d->currentProfile) {
emit currentProfileChanged();
}
}
void KisInputProfileManager::saveProfiles()
{
QString storagePath = KGlobal::dirs()->saveLocation("data", "krita/input/");
Q_FOREACH(KisInputProfile * p, d->profiles) {
QString fileName = d->profileFileName(p->name());
KConfig config(storagePath + fileName, KConfig::SimpleConfig);
config.group("General").writeEntry("name", p->name());
config.group("General").writeEntry("version", PROFILE_VERSION);
Q_FOREACH(KisAbstractInputAction * action, d->actions) {
KConfigGroup grp = config.group(action->id());
grp.deleteGroup(); //Clear the group of any existing shortcuts.
int index = 0;
QList<KisShortcutConfiguration *> shortcuts = p->shortcutsForAction(action);
Q_FOREACH(KisShortcutConfiguration * shortcut, shortcuts) {
grp.writeEntry(QString("%1").arg(index++), shortcut->serialize());
}
}
config.sync();
}
KisConfig config;
config.setCurrentInputProfile(d->currentProfile->name());
//Force a reload of the current profile in input manager and whatever else uses the profile.
emit currentProfileChanged();
}
void KisInputProfileManager::resetAll()
{
QString kdeHome = KGlobal::dirs()->localkdedir();
QStringList profiles = KGlobal::dirs()->findAllResources("data", "krita/input/*", KStandardDirs::Recursive);
foreach(const QString &profile, profiles) {
if(profile.contains(kdeHome)) {
//This is a local file, remove it.
QFile::remove(profile);
}
}
//Load the profiles again, this should now only load those shipped with Krita.
loadProfiles();
emit profilesChanged();
}
KisInputProfileManager::KisInputProfileManager(QObject *parent)
: QObject(parent), d(new Private())
{
d->createActions();
}
KisInputProfileManager::~KisInputProfileManager()
{
qDeleteAll(d->profiles);
qDeleteAll(d->actions);
delete d;
}
void KisInputProfileManager::Private::createActions()
{
//TODO: Make this plugin based
//Note that the ordering here determines how things show up in the UI
actions.append(new KisToolInvocationAction());
actions.append(new KisAlternateInvocationAction());
actions.append(new KisChangePrimarySettingAction());
actions.append(new KisPanAction());
actions.append(new KisRotateCanvasAction());
actions.append(new KisZoomAction());
actions.append(new KisShowPaletteAction());
actions.append(new KisSelectLayerAction());
actions.append(new KisGammaExposureAction());
}
QString KisInputProfileManager::Private::profileFileName(const QString &profileName)
{
return profileName.toLower().remove(QRegExp("[^a-z0-9]")).append(".profile");
}
diff --git a/krita/ui/input/kis_pan_action.cpp b/krita/ui/input/kis_pan_action.cpp
index bf05e8833b9..37ab48baf0c 100644
--- a/krita/ui/input/kis_pan_action.cpp
+++ b/krita/ui/input/kis_pan_action.cpp
@@ -1,167 +1,167 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_pan_action.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QMouseEvent>
#include <QApplication>
#include <QGesture>
#include <klocalizedstring.h>
#include <KoCanvasController.h>
#include <kis_canvas2.h>
#include "kis_input_manager.h"
class KisPanAction::Private
{
public:
Private() : panDistance(10) { }
QPointF averagePoint( QTouchEvent* event );
const int panDistance;
QPointF lastPosition;
};
KisPanAction::KisPanAction()
: KisAbstractInputAction("Pan Canvas")
, d(new Private)
{
setName(i18n("Pan Canvas"));
setDescription(i18n("The <i>Pan Canvas</i> action pans the canvas."));
QHash<QString, int> shortcuts;
shortcuts.insert(i18n("Pan Mode"), PanModeShortcut);
shortcuts.insert(i18n("Pan Left"), PanLeftShortcut);
shortcuts.insert(i18n("Pan Right"), PanRightShortcut);
shortcuts.insert(i18n("Pan Up"), PanUpShortcut);
shortcuts.insert(i18n("Pan Down"), PanDownShortcut);
setShortcutIndexes(shortcuts);
}
KisPanAction::~KisPanAction()
{
delete d;
}
int KisPanAction::priority() const
{
return 5;
}
void KisPanAction::activate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::setOverrideCursor(Qt::OpenHandCursor);
}
void KisPanAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisPanAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
switch (shortcut) {
case PanModeShortcut: {
QTouchEvent *tevent = dynamic_cast<QTouchEvent*>(event);
if(tevent)
d->lastPosition = d->averagePoint(tevent);
break;
}
case PanLeftShortcut:
inputManager()->canvas()->canvasController()->pan(QPoint(d->panDistance, 0));
break;
case PanRightShortcut:
inputManager()->canvas()->canvasController()->pan(QPoint(-d->panDistance, 0));
break;
case PanUpShortcut:
inputManager()->canvas()->canvasController()->pan(QPoint(0, d->panDistance));
break;
case PanDownShortcut:
inputManager()->canvas()->canvasController()->pan(QPoint(0, -d->panDistance));
break;
}
}
void KisPanAction::inputEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::Gesture: {
QGestureEvent *gevent = static_cast<QGestureEvent*>(event);
if (gevent->activeGestures().at(0)->gestureType() == Qt::PanGesture) {
QPanGesture *pan = static_cast<QPanGesture*>(gevent->activeGestures().at(0));
inputManager()->canvas()->canvasController()->pan(-pan->delta().toPoint() * 0.2);
}
}
case QEvent::TouchUpdate: {
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
QPointF newPos = d->averagePoint(tevent);
QPointF delta = newPos - d->lastPosition;
// If this is enormously large, then we are likely in the process of ending the gesture,
// with fingers being lifted one by one from the perspective of our very speedy operations,
// and as such, ignore those big jumps.
if(delta.manhattanLength() < 50) {
inputManager()->canvas()->canvasController()->pan(-delta.toPoint());
d->lastPosition = newPos;
}
}
default:
break;
}
KisAbstractInputAction::inputEvent(event);
}
-void KisPanAction::mouseMoved(const QPointF &lastPos, const QPointF &pos)
+void KisPanAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
QPointF relMovement = -(pos - lastPos);
inputManager()->canvas()->canvasController()->pan(relMovement.toPoint());
}
QPointF KisPanAction::Private::averagePoint( QTouchEvent* event )
{
QPointF result;
int count = 0;
foreach( QTouchEvent::TouchPoint point, event->touchPoints() ) {
if( point.state() != Qt::TouchPointReleased ) {
result += point.screenPos();
count++;
}
}
if( count > 0 ) {
return result / count;
} else {
return QPointF();
}
}
bool KisPanAction::isShortcutRequired(int shortcut) const
{
return shortcut == PanModeShortcut;
}
diff --git a/krita/ui/input/kis_pan_action.h b/krita/ui/input/kis_pan_action.h
index bad44b0443a..99aa072d7d8 100644
--- a/krita/ui/input/kis_pan_action.h
+++ b/krita/ui/input/kis_pan_action.h
@@ -1,63 +1,63 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_PAN_ACTION_H
#define KIS_PAN_ACTION_H
#include "kis_abstract_input_action.h"
/**
* \brief Pan Canvas implementation of KisAbstractInputAction.
*
* The Pan Canvas action pans the canvas.
*/
class KisPanAction : public KisAbstractInputAction
{
public:
/**
* The different behaviours for this action.
*/
enum Shortcut {
PanModeShortcut, ///< Toggle the pan mode.
PanLeftShortcut, ///< Pan left by a fixed amount.
PanRightShortcut, ///< Pan right by a fixed amount.
PanUpShortcut, ///< Pan up by a fixed amount.
PanDownShortcut ///< Pan down by a fixed amount.
};
explicit KisPanAction();
virtual ~KisPanAction();
virtual int priority() const;
void activate(int shortcut);
void deactivate(int shortcut);
void begin(int shortcut, QEvent *event);
virtual void inputEvent(QEvent* event);
- void mouseMoved(const QPointF &lastPos, const QPointF &pos);
+ void cursorMoved(const QPointF &lastPos, const QPointF &pos);
virtual bool isShortcutRequired(int shortcut) const;
private:
class Private;
Private * const d;
};
#endif // KIS_PAN_ACTION_H
diff --git a/krita/ui/input/kis_rotate_canvas_action.cpp b/krita/ui/input/kis_rotate_canvas_action.cpp
index 400d2f9e408..21611b48472 100644
--- a/krita/ui/input/kis_rotate_canvas_action.cpp
+++ b/krita/ui/input/kis_rotate_canvas_action.cpp
@@ -1,132 +1,132 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_rotate_canvas_action.h"
#include <QApplication>
#include <klocalizedstring.h>
#include "kis_cursor.h"
#include "kis_canvas_controller.h"
#include <kis_canvas2.h>
#include "kis_input_manager.h"
#include <math.h>
class KisRotateCanvasAction::Private
{
public:
Private() : angleDrift(0) {}
Shortcut mode;
qreal angleDrift;
};
KisRotateCanvasAction::KisRotateCanvasAction()
: KisAbstractInputAction("Rotate Canvas")
, d(new Private())
{
setName(i18n("Rotate Canvas"));
setDescription(i18n("The <i>Rotate Canvas</i> action rotates the canvas."));
QHash<QString, int> shortcuts;
shortcuts.insert(i18n("Rotate Mode"), RotateModeShortcut);
shortcuts.insert(i18n("Discrete Rotate Mode"), DiscreteRotateModeShortcut);
shortcuts.insert(i18n("Rotate Left"), RotateLeftShortcut);
shortcuts.insert(i18n("Rotate Right"), RotateRightShortcut);
shortcuts.insert(i18n("Reset Rotation"), RotateResetShortcut);
setShortcutIndexes(shortcuts);
}
KisRotateCanvasAction::~KisRotateCanvasAction()
{
delete d;
}
int KisRotateCanvasAction::priority() const
{
return 3;
}
void KisRotateCanvasAction::activate(int shortcut)
{
if (shortcut == DiscreteRotateModeShortcut) {
QApplication::setOverrideCursor(KisCursor::rotateCanvasDiscreteCursor());
} else /* if (shortcut == SmoothRotateModeShortcut) */ {
QApplication::setOverrideCursor(KisCursor::rotateCanvasSmoothCursor());
}
}
void KisRotateCanvasAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisRotateCanvasAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
KisCanvasController *canvasController =
dynamic_cast<KisCanvasController*>(inputManager()->canvas()->canvasController());
switch(shortcut) {
case RotateModeShortcut:
d->mode = (Shortcut)shortcut;
break;
case DiscreteRotateModeShortcut:
d->mode = (Shortcut)shortcut;
d->angleDrift = 0;
break;
case RotateLeftShortcut:
canvasController->rotateCanvasLeft15();
break;
case RotateRightShortcut:
canvasController->rotateCanvasRight15();
break;
case RotateResetShortcut:
canvasController->resetCanvasRotation();
break;
}
}
-void KisRotateCanvasAction::mouseMoved(const QPointF &lastPos, const QPointF &pos)
+void KisRotateCanvasAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
const KisCoordinatesConverter *converter = inputManager()->canvas()->coordinatesConverter();
QPointF centerPoint = converter->flakeToWidget(converter->flakeCenterPoint());
QPointF oldPoint = lastPos - centerPoint;
QPointF newPoint = pos - centerPoint;
qreal oldAngle = atan2(oldPoint.y(), oldPoint.x());
qreal newAngle = atan2(newPoint.y(), newPoint.x());
qreal angle = (180 / M_PI) * (newAngle - oldAngle);
if (d->mode == DiscreteRotateModeShortcut) {
const qreal angleStep = 15;
qreal initialAngle = inputManager()->canvas()->rotationAngle();
qreal roundedAngle = qRound((initialAngle + angle + d->angleDrift) / angleStep) * angleStep - initialAngle;
d->angleDrift += angle - roundedAngle;
angle = roundedAngle;
}
KisCanvasController *canvasController =
dynamic_cast<KisCanvasController*>(inputManager()->canvas()->canvasController());
canvasController->rotateCanvas(angle);
}
diff --git a/krita/ui/input/kis_rotate_canvas_action.h b/krita/ui/input/kis_rotate_canvas_action.h
index 0c40c536ca8..3e9c53d60d9 100644
--- a/krita/ui/input/kis_rotate_canvas_action.h
+++ b/krita/ui/input/kis_rotate_canvas_action.h
@@ -1,59 +1,59 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ROTATE_CANVAS_ACTION_H
#define KIS_ROTATE_CANVAS_ACTION_H
#include "kis_abstract_input_action.h"
/**
* \brief Rotate Canvas implementation of KisAbstractInputAction.
*
* The Rotate Canvas action rotates the canvas.
*/
class KisRotateCanvasAction : public KisAbstractInputAction
{
public:
/**
* The different behaviours for this action.
*/
enum Shortcut {
RotateModeShortcut, ///< Toggle Rotate mode.
DiscreteRotateModeShortcut, ///< Toggle Discrete Rotate mode.
RotateLeftShortcut, ///< Rotate left by a fixed amount.
RotateRightShortcut, ///< Rotate right by a fixed amount.
RotateResetShortcut ///< Reset the rotation to 0.
};
explicit KisRotateCanvasAction();
virtual ~KisRotateCanvasAction();
virtual int priority() const;
void activate(int shortcut);
void deactivate(int shortcut);
void begin(int shortcut, QEvent *event);
- void mouseMoved(const QPointF &lastPos, const QPointF &pos);
+ void cursorMoved(const QPointF &lastPos, const QPointF &pos);
private:
class Private;
Private * const d;
};
#endif // KIS_ROTATE_CANVAS_ACTION_H
diff --git a/krita/ui/input/kis_select_layer_action.cpp b/krita/ui/input/kis_select_layer_action.cpp
index 3344715d7ad..b27e4f13c87 100644
--- a/krita/ui/input/kis_select_layer_action.cpp
+++ b/krita/ui/input/kis_select_layer_action.cpp
@@ -1,101 +1,100 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_select_layer_action.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QMouseEvent>
#include <QApplication>
#include <klocalizedstring.h>
#include <kis_canvas2.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include <kis_node_manager.h>
#include <kis_cursor.h>
#include "kis_input_manager.h"
#include "kis_tool_utils.h"
class KisSelectLayerAction::Private
{
public:
};
KisSelectLayerAction::KisSelectLayerAction()
: KisAbstractInputAction("Select Layer")
, d(new Private)
{
setName(i18n("Select Layer"));
setDescription(i18n("Selects a layer under cursor position"));
QHash<QString, int> shortcuts;
shortcuts.insert(i18n("Select Layer Mode"), SelectLayerModeShortcut);
setShortcutIndexes(shortcuts);
}
KisSelectLayerAction::~KisSelectLayerAction()
{
delete d;
}
int KisSelectLayerAction::priority() const
{
return 5;
}
void KisSelectLayerAction::activate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::setOverrideCursor(KisCursor::pickLayerCursor());
}
void KisSelectLayerAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisSelectLayerAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
switch (shortcut) {
case SelectLayerModeShortcut:
inputEvent(event);
break;
}
}
void KisSelectLayerAction::inputEvent(QEvent *event)
{
- QMouseEvent *mouseEvent;
- if (event && (mouseEvent = dynamic_cast<QMouseEvent*>(event))) {
+ if (event && (event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove)) {
QPoint pos =
inputManager()->canvas()->
- coordinatesConverter()->widgetToImage(mouseEvent->posF()).toPoint();
+ coordinatesConverter()->widgetToImage(eventPosF(event)).toPoint();
KisNodeSP node = KisToolUtils::findNode(inputManager()->canvas()->image()->root(), pos, false);
if (node) {
inputManager()->canvas()->viewManager()->nodeManager()->slotNonUiActivatedNode(node);
}
}
}
diff --git a/krita/ui/input/kis_shortcut_matcher.cpp b/krita/ui/input/kis_shortcut_matcher.cpp
index e81128650c0..28e596d1085 100644
--- a/krita/ui/input/kis_shortcut_matcher.cpp
+++ b/krita/ui/input/kis_shortcut_matcher.cpp
@@ -1,492 +1,505 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_shortcut_matcher.h"
+#include <QEvent>
#include <QMouseEvent>
#include <QTabletEvent>
#include "kis_abstract_input_action.h"
#include "kis_stroke_shortcut.h"
#include "kis_touch_shortcut.h"
#ifdef DEBUG_MATCHER
-#include <QDebug>
-#define DEBUG_ACTION(action) qDebug() << __FUNCTION__ << ":" << action;
-#define DEBUG_BUTTON_ACTION(action, button) qDebug() << __FUNCTION__ << ":" << action << "button:" << button << "btns:" << m_d->buttons << "keys:" << m_d->keys;
+#include <kis_debug.h>
+#define DEBUG_ACTION(action) dbgKrita << __FUNCTION__ << ":" << action;
+#define DEBUG_BUTTON_ACTION(action, button) dbgKrita << __FUNCTION__ << ":" << action << "button:" << button << "btns:" << m_d->buttons << "keys:" << m_d->keys;
+#define DEBUG_EVENT_ACTION(action, event) if (event) {dbgKrita << __FUNCTION__ << ":" << action << "type:" << event->type();}
#else
#define DEBUG_ACTION(action)
#define DEBUG_BUTTON_ACTION(action, button)
+#define DEBUG_EVENT_ACTION(action, event)
#endif
class Q_DECL_HIDDEN KisShortcutMatcher::Private
{
public:
Private()
: runningShortcut(0)
, readyShortcut(0)
, touchShortcut(0)
, suppressAllActions(false)
, cursorEntered(false)
, usingTouch(false)
{}
QList<KisSingleActionShortcut*> singleActionShortcuts;
QList<KisStrokeShortcut*> strokeShortcuts;
QList<KisTouchShortcut*> touchShortcuts;
QList<Qt::Key> keys;
QList<Qt::MouseButton> buttons;
KisStrokeShortcut *runningShortcut;
KisStrokeShortcut *readyShortcut;
QList<KisStrokeShortcut*> readyShortcuts;
KisTouchShortcut *touchShortcut;
bool suppressAllActions;
bool cursorEntered;
bool usingTouch;
inline bool actionsSuppressed() const {
return suppressAllActions || !cursorEntered;
}
};
KisShortcutMatcher::KisShortcutMatcher()
: m_d(new Private)
{
m_d->runningShortcut = 0;
m_d->readyShortcut = 0;
}
KisShortcutMatcher::~KisShortcutMatcher()
{
qDeleteAll(m_d->singleActionShortcuts);
qDeleteAll(m_d->strokeShortcuts);
delete m_d;
}
void KisShortcutMatcher::addShortcut(KisSingleActionShortcut *shortcut)
{
m_d->singleActionShortcuts.append(shortcut);
}
void KisShortcutMatcher::addShortcut(KisStrokeShortcut *shortcut)
{
m_d->strokeShortcuts.append(shortcut);
}
void KisShortcutMatcher::addShortcut( KisTouchShortcut* shortcut )
{
m_d->touchShortcuts.append(shortcut);
}
bool KisShortcutMatcher::supportsHiResInputEvents()
{
return
m_d->runningShortcut &&
m_d->runningShortcut->action() &&
m_d->runningShortcut->action()->supportsHiResInputEvents();
}
bool KisShortcutMatcher::keyPressed(Qt::Key key)
{
bool retval = false;
if (m_d->keys.contains(key)) reset();
if (!m_d->runningShortcut) {
retval = tryRunKeyShortcut(key, 0);
}
m_d->keys.append(key);
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
return retval;
}
bool KisShortcutMatcher::autoRepeatedKeyPressed(Qt::Key key)
{
bool retval = false;
if (!m_d->keys.contains(key)) reset();
if (!m_d->runningShortcut) {
retval = tryRunKeyShortcut(key, 0);
}
return retval;
}
bool KisShortcutMatcher::keyReleased(Qt::Key key)
{
if (!m_d->keys.contains(key)) reset();
else m_d->keys.removeOne(key);
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
return false;
}
-bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QMouseEvent *event)
+bool KisShortcutMatcher::buttonPressed(Qt::MouseButton button, QEvent *event)
{
DEBUG_BUTTON_ACTION("entered", button);
bool retval = false;
if (m_d->usingTouch) {
return retval;
}
if (m_d->buttons.contains(button)) reset();
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
retval = tryRunReadyShortcut(button, event);
}
m_d->buttons.append(button);
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
return retval;
}
-bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QMouseEvent *event)
+bool KisShortcutMatcher::buttonReleased(Qt::MouseButton button, QEvent *event)
{
DEBUG_BUTTON_ACTION("entered", button);
bool retval = false;
if (m_d->usingTouch) {
return retval;
}
if (m_d->runningShortcut) {
retval = tryEndRunningShortcut(button, event);
DEBUG_BUTTON_ACTION("ended", button);
}
if (!m_d->buttons.contains(button)) reset();
else m_d->buttons.removeOne(button);
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
return retval;
}
bool KisShortcutMatcher::wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
{
- if (m_d->runningShortcut || m_d->usingTouch) return false;
+ if (m_d->runningShortcut || m_d->usingTouch) {
+ DEBUG_ACTION("Wheel event canceled.");
+ return false;
+ }
return tryRunWheelShortcut(wheelAction, event);
}
-bool KisShortcutMatcher::mouseMoved(QMouseEvent *event)
+bool KisShortcutMatcher::pointerMoved(QEvent *event)
{
if (m_d->usingTouch || !m_d->runningShortcut) {
return false;
}
m_d->runningShortcut->action()->inputEvent(event);
return true;
}
void KisShortcutMatcher::enterEvent()
{
m_d->cursorEntered = true;
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
}
void KisShortcutMatcher::leaveEvent()
{
m_d->cursorEntered = false;
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
}
bool KisShortcutMatcher::touchBeginEvent( QTouchEvent* event )
{
Q_UNUSED(event)
return true;
}
bool KisShortcutMatcher::touchUpdateEvent( QTouchEvent* event )
{
bool retval = false;
if( m_d->touchShortcut && !m_d->touchShortcut->match( event ) ) {
retval = tryEndTouchShortcut( event );
}
if( !m_d->touchShortcut ) {
retval = tryRunTouchShortcut( event );
} else {
m_d->touchShortcut->action()->inputEvent( event );
retval = true;
}
return retval;
}
bool KisShortcutMatcher::touchEndEvent( QTouchEvent* event )
{
m_d->usingTouch = false; // we need to say we are done because qt will not send further event
// we should try and end the shortcut too (it might be that there is none? (sketch))
if( tryEndTouchShortcut( event ) ) {
return true;
}
return false;
}
Qt::MouseButtons listToFlags(const QList<Qt::MouseButton> &list) {
Qt::MouseButtons flags;
foreach (Qt::MouseButton b, list) {
flags |= b;
}
return flags;
}
void KisShortcutMatcher::reinitialize()
{
reset();
if (!m_d->runningShortcut) {
prepareReadyShortcuts();
tryActivateReadyShortcut();
}
}
void KisShortcutMatcher::reset()
{
m_d->keys.clear();
m_d->buttons.clear();
DEBUG_ACTION("reset!");
}
void KisShortcutMatcher::suppressAllActions(bool value)
{
m_d->suppressAllActions = value;
}
void KisShortcutMatcher::clearShortcuts()
{
reset();
qDeleteAll(m_d->singleActionShortcuts);
m_d->singleActionShortcuts.clear();
qDeleteAll(m_d->strokeShortcuts);
m_d->strokeShortcuts.clear();
m_d->readyShortcuts.clear();
m_d->runningShortcut = 0;
m_d->readyShortcut = 0;
}
bool KisShortcutMatcher::tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event)
{
return tryRunSingleActionShortcutImpl(wheelAction, event, m_d->keys);
}
bool KisShortcutMatcher::tryRunKeyShortcut(Qt::Key key, QKeyEvent *event)
{
/**
* Handle the case of autorepeated keys.
* The autorepeated key should not be present in modifiers.
*/
QList<Qt::Key> filteredKeys = m_d->keys;
filteredKeys.removeOne(key);
return tryRunSingleActionShortcutImpl(key, event, filteredKeys);
}
+// Note: sometimes event can be zero!!
template<typename T, typename U>
bool KisShortcutMatcher::tryRunSingleActionShortcutImpl(T param, U *event, const QList<Qt::Key> &keysState)
{
- if (m_d->actionsSuppressed()) return false;
+ if (m_d->actionsSuppressed()) {
+ DEBUG_EVENT_ACTION("Event suppressed", event)
+ return false;
+ }
KisSingleActionShortcut *goodCandidate = 0;
foreach(KisSingleActionShortcut *s, m_d->singleActionShortcuts) {
if(s->match(keysState, param) &&
(!goodCandidate || s->priority() > goodCandidate->priority())) {
goodCandidate = s;
}
}
if (goodCandidate) {
+ DEBUG_EVENT_ACTION("Beginning action for event", event)
goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event);
goodCandidate->action()->end(0);
+ } else {
+ DEBUG_EVENT_ACTION("Could not match a candidate for event", event)
}
return goodCandidate;
}
void KisShortcutMatcher::prepareReadyShortcuts()
{
m_d->readyShortcuts.clear();
if (m_d->actionsSuppressed()) return;
foreach(KisStrokeShortcut *s, m_d->strokeShortcuts) {
if (s->matchReady(m_d->keys, m_d->buttons)) {
m_d->readyShortcuts.append(s);
}
}
}
-bool KisShortcutMatcher::tryRunReadyShortcut( Qt::MouseButton button, QMouseEvent* event )
+bool KisShortcutMatcher::tryRunReadyShortcut( Qt::MouseButton button, QEvent* event )
{
KisStrokeShortcut *goodCandidate = 0;
foreach(KisStrokeShortcut *s, m_d->readyShortcuts) {
if (s->matchBegin(button) &&
(!goodCandidate || s->priority() > goodCandidate->priority())) {
goodCandidate = s;
}
}
if (goodCandidate) {
if (m_d->readyShortcut) {
if (m_d->readyShortcut != goodCandidate) {
m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex());
goodCandidate->action()->activate(goodCandidate->shortcutIndex());
}
m_d->readyShortcut = 0;
} else {
goodCandidate->action()->activate(goodCandidate->shortcutIndex());
}
goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event);
m_d->runningShortcut = goodCandidate;
}
return goodCandidate;
}
void KisShortcutMatcher::tryActivateReadyShortcut()
{
KisStrokeShortcut *goodCandidate = 0;
foreach(KisStrokeShortcut *s, m_d->readyShortcuts) {
if (!goodCandidate || s->priority() > goodCandidate->priority()) {
goodCandidate = s;
}
}
if (goodCandidate) {
if (m_d->readyShortcut && m_d->readyShortcut != goodCandidate) {
m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex());
m_d->readyShortcut = 0;
}
if (!m_d->readyShortcut) {
goodCandidate->action()->activate(goodCandidate->shortcutIndex());
m_d->readyShortcut = goodCandidate;
}
} else if (m_d->readyShortcut) {
m_d->readyShortcut->action()->deactivate(m_d->readyShortcut->shortcutIndex());
m_d->readyShortcut = 0;
}
}
-bool KisShortcutMatcher::tryEndRunningShortcut( Qt::MouseButton button, QMouseEvent* event )
+bool KisShortcutMatcher::tryEndRunningShortcut( Qt::MouseButton button, QEvent* event )
{
Q_ASSERT(m_d->runningShortcut);
Q_ASSERT(!m_d->readyShortcut);
if (m_d->runningShortcut->matchBegin(button)) {
if (m_d->runningShortcut->action()) {
KisAbstractInputAction* action = m_d->runningShortcut->action();
int shortcutIndex = m_d->runningShortcut->shortcutIndex();
action->end(event);
action->deactivate(shortcutIndex);
}
m_d->runningShortcut = 0;
}
return !m_d->runningShortcut;
}
bool KisShortcutMatcher::tryRunTouchShortcut( QTouchEvent* event )
{
KisTouchShortcut *goodCandidate = 0;
if (m_d->actionsSuppressed())
return false;
foreach(KisTouchShortcut* shortcut, m_d->touchShortcuts) {
if( shortcut->match( event ) && (!goodCandidate || shortcut->priority() > goodCandidate->priority()) ) {
goodCandidate = shortcut;
}
}
if( goodCandidate ) {
if( m_d->runningShortcut ) {
QMouseEvent mouseEvent(QEvent::MouseButtonRelease,
event->touchPoints().at(0).pos().toPoint(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
tryEndRunningShortcut(Qt::LeftButton, &mouseEvent);
}
goodCandidate->action()->activate(goodCandidate->shortcutIndex());
goodCandidate->action()->begin(goodCandidate->shortcutIndex(), event);
m_d->touchShortcut = goodCandidate;
m_d->usingTouch = true;
}
return goodCandidate;
}
bool KisShortcutMatcher::tryEndTouchShortcut( QTouchEvent* event )
{
if(m_d->touchShortcut) {
m_d->touchShortcut->action()->end(event);
m_d->touchShortcut->action()->deactivate(m_d->touchShortcut->shortcutIndex());
m_d->touchShortcut = 0;
return true;
}
return false;
}
diff --git a/krita/ui/input/kis_shortcut_matcher.h b/krita/ui/input/kis_shortcut_matcher.h
index 4f6fc1cf98b..3abb395d067 100644
--- a/krita/ui/input/kis_shortcut_matcher.h
+++ b/krita/ui/input/kis_shortcut_matcher.h
@@ -1,229 +1,230 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_SHORTCUT_MATCHER_H
#define __KIS_SHORTCUT_MATCHER_H
#include "kis_abstract_shortcut.h"
#include <QList>
#include "kis_single_action_shortcut.h"
+class QEvent;
class QMouseEvent;
class QKeyEvent;
class QWheelEvent;
class QTouchEvent;
class KisStrokeShortcut;
class KisTouchShortcut;
/**
* The class that manages connections between shortcuts and actions.
*
* It processes input events and generates state transitions for the
* actions basing on the data, represented by the shortcuts.
*
* The class works with two types of actions: long running
* (represented by KisStrokeShortcuts) and "atomic"
* (KisSingleActionShortcut). The former one invole some long
* interaction with the user by means of a mouse cursor or a tablet,
* the latter one simple action like "Zoom 100%" or "Reset Rotation".
*
* The single action shortcuts are handled quite easily. The matcher
* listens to the events coming, manages two lists of the pressed keys
* and buttons and when their content corresponds to some single
* action shortcut it just runs this shortcut once.
*
* The strategy for handling the stroke shortcuts is a bit more
* complex. Each such action may be in one of the three states:
*
* Idle <-> Ready <-> Running
*
* In "Idle" state the action is completely inactive and has no access
* to the user
*
* When the action is in "Ready" state, it means that all the
* modifiers for the action are already pressed and we are only
* waiting for a user to press the mouse button and start a stroke. In
* this state the action can show the user its Cursor to notify the user
* what is going to happen next.
*
* In the "Running" state, the action has full access to the user
* input and is considered to perform all the work it was created for.
*
* To implement such state transitions for the actions,
* KisShortcutMatcher first forms a list of the actions which can be
* moved to a ready state (m_d->readyShortcuts), then chooses the one
* with the highest priority to be the only shortcut in the "Ready"
* state and activates it (m_d->readyShortcut). Then when the user
* presses the mouse button, the matcher looks through the list of
* ready shortcuts, chooses which will be running now, deactivates (if
* needed) currently activated action and starts the chosen one.
*
* \see KisSingleActionShortcut
* \see KisStrokeShortcut
*/
class KRITAUI_EXPORT KisShortcutMatcher
{
public:
KisShortcutMatcher();
~KisShortcutMatcher();
void addShortcut(KisSingleActionShortcut *shortcut);
void addShortcut(KisStrokeShortcut *shortcut);
void addShortcut(KisTouchShortcut *shortcut);
/**
* Returns true if the currently running shortcut supports
* processing hi resolution flow of events from the tablet
* device. In most of the cases (except of the painting itself)
* too many events make the execution of the action too slow, so
* the action can decide whether it needs it.
*/
bool supportsHiResInputEvents();
/**
* Handles a key press event.
* No autorepeat events should be passed to this method.
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
bool keyPressed(Qt::Key key);
/**
* Handles a key press event that has been generated by the
* autorepeat.
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
bool autoRepeatedKeyPressed(Qt::Key key);
/**
* Handles a key release event.
* No autorepeat events should be passed to this method.
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
bool keyReleased(Qt::Key key);
/**
- * Handles the mouse button press event
+ * Handles button presses from a tablet or mouse.
*
- * \param button the button that has been pressed
- * \param event the event that caused this call
+ * \param event the event that caused this call.
+ * Must be of type QTabletEvent or QMouseEvent.
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
- bool buttonPressed(Qt::MouseButton button, QMouseEvent *event);
+ bool buttonPressed(Qt::MouseButton button, QEvent *event);
/**
* Handles the mouse button release event
*
- * \param button the button that has been pressed
- * \param event the event that caused this call
+ * \param event the event that caused this call.
+ * Must be of type QTabletEvent or QMouseEvent.
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
- bool buttonReleased(Qt::MouseButton button, QMouseEvent *event);
+ bool buttonReleased(Qt::MouseButton button, QEvent *event);
/**
* Handles the mouse wheel event
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event);
/**
- * Handles the mouse move event
+ * Handles tablet and mouse move events.
*
* \param event the event that caused this call
*
* \return whether the event has been handled successfully and
* should be eaten by the events filter
*/
- bool mouseMoved(QMouseEvent *event);
+ bool pointerMoved(QEvent *event);
/**
* Handle cursor's Enter event.
* We never eat it because it might be used by someone else
*/
void enterEvent();
/**
* Handle cursor's Leave event.
* We never eat it because it might be used by someone else
*/
void leaveEvent();
bool touchBeginEvent(QTouchEvent *event);
bool touchUpdateEvent(QTouchEvent *event);
bool touchEndEvent(QTouchEvent *event);
/**
* Resets the internal state of the matcher and activates the
* prepared action if possible.
*
* This should be done when the window has lost the focus for
* some time, so that several events could be lost
*/
void reinitialize();
/**
* Disables the start of any actions.
*
* WARNING: the actions that has been started before this call
* will *not* be ended. They will be ended in their usual way,
* when the mouse button will be released.
*/
void suppressAllActions(bool value);
/**
* Remove all shortcuts that have been registered.
*/
void clearShortcuts();
private:
friend class KisInputManagerTest;
void reset();
bool tryRunKeyShortcut(Qt::Key key, QKeyEvent *event);
bool tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event);
template<typename T, typename U> bool tryRunSingleActionShortcutImpl(T param, U *event, const QList<Qt::Key> &keysState);
void prepareReadyShortcuts();
- bool tryRunReadyShortcut( Qt::MouseButton button, QMouseEvent* event );
+ bool tryRunReadyShortcut( Qt::MouseButton button, QEvent* event );
void tryActivateReadyShortcut();
- bool tryEndRunningShortcut( Qt::MouseButton button, QMouseEvent* event );
+ bool tryEndRunningShortcut( Qt::MouseButton button, QEvent* event );
bool tryRunTouchShortcut(QTouchEvent *event);
bool tryEndTouchShortcut(QTouchEvent *event);
private:
class Private;
Private * const m_d;
};
#endif /* __KIS_SHORTCUT_MATCHER_H */
diff --git a/krita/ui/input/kis_tablet_debugger.cpp b/krita/ui/input/kis_tablet_debugger.cpp
index 10a2809fd51..02f3de7233f 100644
--- a/krita/ui/input/kis_tablet_debugger.cpp
+++ b/krita/ui/input/kis_tablet_debugger.cpp
@@ -1,230 +1,224 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tablet_debugger.h"
#include <QEvent>
-#include <QDebug>
+#include <QMessageBox>
+#include <kis_debug.h>
#include <kglobal.h>
+#include <QMessageBox>
inline QString button(const QWheelEvent &ev) {
Q_UNUSED(ev);
return "-";
}
-inline QString button(const QTabletEvent &ev) {
- Q_UNUSED(ev);
- return "-";
-}
-
-inline QString buttons(const QTabletEvent &ev) {
- Q_UNUSED(ev);
- return "-";
-}
-
template <class T>
QString button(const T &ev) {
return QString::number(ev.button());
}
template <class T>
QString buttons(const T &ev) {
return QString::number(ev.buttons());
}
template <class Event>
void dumpBaseParams(QTextStream &s, const Event &ev, const QString &prefix)
{
- s << qSetFieldWidth(10) << left << prefix << reset << " ";
- s << qSetFieldWidth(17) << left << exTypeToString(ev.type()) << reset;
+ s << qSetFieldWidth(5) << left << prefix << reset << " ";
+ s << qSetFieldWidth(17) << left << KisTabletDebugger::exTypeToString(ev.type()) << reset;
}
template <class Event>
void dumpMouseRelatedParams(QTextStream &s, const Event &ev)
{
s << "btn: " << button(ev) << " ";
s << "btns: " << buttons(ev) << " ";
s << "pos: " << qSetFieldWidth(4) << ev.x() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.y() << qSetFieldWidth(0) << " ";
s << "gpos: " << qSetFieldWidth(4) << ev.globalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(4) << ev.globalY() << qSetFieldWidth(0) << " ";
}
-QString exTypeToString(QEvent::Type type) {
+QString KisTabletDebugger::exTypeToString(QEvent::Type type) {
return
type == QEvent::TabletEnterProximity ? "TabletEnterProximity" :
type == QEvent::TabletLeaveProximity ? "TabletLeaveProximity" :
type == QEvent::Enter ? "Enter" :
type == QEvent::Leave ? "Leave" :
type == QEvent::FocusIn ? "FocusIn" :
type == QEvent::Wheel ? "Wheel" :
type == QEvent::KeyPress ? "KeyPress" :
type == QEvent::KeyRelease ? "KeyRelease" :
type == QEvent::ShortcutOverride ? "ShortcutOverride" :
type == QMouseEvent::MouseButtonPress ? "MouseButtonPress" :
type == QMouseEvent::MouseButtonRelease ? "MouseButtonRelease" :
type == QMouseEvent::MouseButtonDblClick ? "MouseButtonDblClick" :
type == QMouseEvent::MouseMove ? "MouseMove" :
type == QTabletEvent::TabletMove ? "TabletMove" :
type == QTabletEvent::TabletPress ? "TabletPress" :
type == QTabletEvent::TabletRelease ? "TabletRelease" :
- type == (QEvent::Type) KisTabletEvent::TabletMoveEx ? "TabletMoveEx" :
- type == (QEvent::Type) KisTabletEvent::TabletPressEx ? "TabletPressEx" :
- type == (QEvent::Type) KisTabletEvent::TabletReleaseEx ? "TabletReleaseEx" :
"unknown";
}
KisTabletDebugger::KisTabletDebugger()
: m_debugEnabled(false)
{
}
KisTabletDebugger* KisTabletDebugger::instance()
{
K_GLOBAL_STATIC(KisTabletDebugger, s_instance);
return s_instance;
}
void KisTabletDebugger::toggleDebugging()
{
m_debugEnabled = !m_debugEnabled;
+ QMessageBox::information(0, i18nc("@title:window", "Krita"), m_debugEnabled ?
+ i18n("Tablet Event Logging Enabled") :
+ i18n("Tablet Event Logging Disabled"));
+ if (m_debugEnabled) {
+ dbgKrita << "vvvvvvvvvvvvvvvvvvvvvvv START TABLET EVENT LOG vvvvvvvvvvvvvvvvvvvvvvv";
+ }
+ else {
+ dbgKrita << "^^^^^^^^^^^^^^^^^^^^^^^ START TABLET EVENT LOG ^^^^^^^^^^^^^^^^^^^^^^^";
+ }
}
bool KisTabletDebugger::debugEnabled() const
{
return m_debugEnabled;
}
bool KisTabletDebugger::initializationDebugEnabled() const
{
// FIXME: make configurable!
return true;
}
bool KisTabletDebugger::debugRawTabletValues() const
{
// FIXME: make configurable!
return m_debugEnabled;
}
QString KisTabletDebugger::eventToString(const QMouseEvent &ev, const QString &prefix)
{
QString string;
QTextStream s(&string);
dumpBaseParams(s, ev, prefix);
dumpMouseRelatedParams(s, ev);
+ s << "Source:" << ev.source();
return string;
}
QString KisTabletDebugger::eventToString(const QKeyEvent &ev, const QString &prefix)
{
QString string;
QTextStream s(&string);
dumpBaseParams(s, ev, prefix);
s << "key: 0x" << hex << ev.key() << reset << " ";
s << "mod: 0x" << hex << ev.modifiers() << reset << " ";
s << "text: " << (ev.text().isEmpty() ? "none" : ev.text());
return string;
}
QString KisTabletDebugger::eventToString(const QWheelEvent &ev, const QString &prefix)
{
QString string;
QTextStream s(&string);
dumpBaseParams(s, ev, prefix);
dumpMouseRelatedParams(s, ev);
s << "delta: " << ev.delta() << " ";
s << "orientation: " << (ev.orientation() == Qt::Horizontal ? "H" : "V") << " ";
return string;
}
QString KisTabletDebugger::eventToString(const QEvent &ev, const QString &prefix)
{
QString string;
QTextStream s(&string);
dumpBaseParams(s, ev, prefix);
return string;
}
template <class Event>
QString tabletEventToString(const Event &ev, const QString &prefix)
{
QString string;
QTextStream s(&string);
dumpBaseParams(s, ev, prefix);
dumpMouseRelatedParams(s, ev);
s << "hires: " << qSetFieldWidth(8) << ev.hiResGlobalX() << qSetFieldWidth(0) << "," << qSetFieldWidth(8) << ev.hiResGlobalY() << qSetFieldWidth(0) << " ";
s << "prs: " << qSetFieldWidth(4) << fixed << ev.pressure() << reset << " ";
s << KisTabletDebugger::tabletDeviceToString((QTabletEvent::TabletDevice) ev.device()) << " ";
s << KisTabletDebugger::pointerTypeToString((QTabletEvent::PointerType) ev.pointerType()) << " ";
s << "id: " << ev.uniqueId() << " ";
s << "xTilt: " << ev.xTilt() << " ";
s << "yTilt: " << ev.yTilt() << " ";
s << "rot: " << ev.rotation() << " ";
s << "z: " << ev.z() << " ";
s << "tp: " << ev.tangentialPressure() << " ";
return string;
}
QString KisTabletDebugger::eventToString(const QTabletEvent &ev, const QString &prefix)
{
return tabletEventToString(ev, prefix);
}
-QString KisTabletDebugger::eventToString(const KisTabletEvent &ev, const QString &prefix)
-{
- return tabletEventToString(ev, prefix);
-}
-
QString KisTabletDebugger::tabletDeviceToString(QTabletEvent::TabletDevice device)
{
return
device == QTabletEvent::NoDevice ? "NoDevice" :
device == QTabletEvent::Puck ? "Puck" :
device == QTabletEvent::Stylus ? "Stylus" :
device == QTabletEvent::Airbrush ? "Airbrush" :
device == QTabletEvent::FourDMouse ? "FourDMouse" :
device == QTabletEvent::XFreeEraser ? "XFreeEraser" :
device == QTabletEvent::RotationStylus ? "RotationStylus" :
"unknown";
}
QString KisTabletDebugger::pointerTypeToString(QTabletEvent::PointerType pointer) {
return
pointer == QTabletEvent::UnknownPointer ? "UnknownPointer" :
pointer == QTabletEvent::Pen ? "Pen" :
pointer == QTabletEvent::Cursor ? "Cursor" :
pointer == QTabletEvent::Eraser ? "Eraser" :
"unknown";
}
diff --git a/krita/ui/input/kis_tablet_debugger.h b/krita/ui/input/kis_tablet_debugger.h
index 0825d724846..0430d6b3d12 100644
--- a/krita/ui/input/kis_tablet_debugger.h
+++ b/krita/ui/input/kis_tablet_debugger.h
@@ -1,54 +1,56 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TABLET_DEBUGGER_H
#define __KIS_TABLET_DEBUGGER_H
#include <QTextStream>
#include <QTabletEvent>
#include <input/kis_tablet_event.h>
+#include <QLoggingCategory>
class KisTabletDebugger
{
public:
static KisTabletDebugger* instance();
void toggleDebugging();
bool debugEnabled() const;
bool initializationDebugEnabled() const;
bool debugRawTabletValues() const;
QString eventToString(const QMouseEvent &ev, const QString &prefix);
QString eventToString(const QKeyEvent &ev, const QString &prefix);
QString eventToString(const QWheelEvent &ev, const QString &prefix);
QString eventToString(const QTabletEvent &ev, const QString &prefix);
QString eventToString(const KisTabletEvent &ev, const QString &prefix);
QString eventToString(const QEvent &ev, const QString &prefix);
static QString tabletDeviceToString(QTabletEvent::TabletDevice device);
static QString pointerTypeToString(QTabletEvent::PointerType pointer);
+ static QString exTypeToString(QEvent::Type type);
private:
KisTabletDebugger();
private:
bool m_debugEnabled;
};
#endif /* __KIS_TABLET_DEBUGGER_H */
diff --git a/krita/ui/input/kis_tool_invocation_action.cpp b/krita/ui/input/kis_tool_invocation_action.cpp
index e548aefea7b..8cf52089635 100644
--- a/krita/ui/input/kis_tool_invocation_action.cpp
+++ b/krita/ui/input/kis_tool_invocation_action.cpp
@@ -1,168 +1,165 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_invocation_action.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoToolManager.h>
#include <kis_tool_proxy.h>
#include <kis_canvas2.h>
#include <kis_coordinates_converter.h>
#include "kis_tool.h"
#include "kis_input_manager.h"
#include "kis_image.h"
class KisToolInvocationAction::Private
{
public:
Private() : active(false) { }
bool active;
};
KisToolInvocationAction::KisToolInvocationAction()
: KisAbstractInputAction("Tool Invocation")
, d(new Private)
{
setName(i18n("Tool Invocation"));
setDescription(i18n("The <i>Tool Invocation</i> action invokes the current tool, for example, using the brush tool, it will start painting."));
QHash<QString, int> indexes;
indexes.insert(i18n("Activate"), ActivateShortcut);
indexes.insert(i18n("Confirm"), ConfirmShortcut);
indexes.insert(i18n("Cancel"), CancelShortcut);
indexes.insert(i18n("Activate Line Tool"), LineToolShortcut);
setShortcutIndexes(indexes);
}
KisToolInvocationAction::~KisToolInvocationAction()
{
delete d;
}
void KisToolInvocationAction::activate(int shortcut)
{
Q_UNUSED(shortcut);
if (!inputManager()) return;
if (shortcut == LineToolShortcut) {
KoToolManager::instance()->switchToolTemporaryRequested("KritaShape/KisToolLine");
}
inputManager()->toolProxy()->activateToolAction(KisTool::Primary);
}
void KisToolInvocationAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
if (!inputManager()) return;
inputManager()->toolProxy()->deactivateToolAction(KisTool::Primary);
if (shortcut == LineToolShortcut) {
KoToolManager::instance()->switchBackRequested();
}
}
int KisToolInvocationAction::priority() const
{
return 0;
}
bool KisToolInvocationAction::canIgnoreModifiers() const
{
return true;
}
void KisToolInvocationAction::begin(int shortcut, QEvent *event)
{
if (shortcut == ActivateShortcut || shortcut == LineToolShortcut) {
d->active =
inputManager()->toolProxy()->forwardEvent(
- KisToolProxy::BEGIN, KisTool::Primary, event, event,
- inputManager()->lastTabletEvent());
+ KisToolProxy::BEGIN, KisTool::Primary, event, event);
} else if (shortcut == ConfirmShortcut) {
QKeyEvent pressEvent(QEvent::KeyPress, Qt::Key_Return, 0);
inputManager()->toolProxy()->keyPressEvent(&pressEvent);
QKeyEvent releaseEvent(QEvent::KeyRelease, Qt::Key_Return, 0);
inputManager()->toolProxy()->keyReleaseEvent(&releaseEvent);
/**
* All the tools now have a KisTool::requestStrokeEnd() method,
* so they should use this instead of direct filtering Enter key
* press. Until all the tools support it, we just duplicate the
* key event and the method call
*/
inputManager()->canvas()->image()->requestStrokeEnd();
} else if (shortcut == CancelShortcut) {
/**
* The tools now have a KisTool::requestStrokeCancellation() method,
* so just request it.
*/
inputManager()->canvas()->image()->requestStrokeCancellation();
}
}
void KisToolInvocationAction::end(QEvent *event)
{
if (d->active) {
inputManager()->toolProxy()->
- forwardEvent(KisToolProxy::END, KisTool::Primary, event, event,
- inputManager()->lastTabletEvent());
+ forwardEvent(KisToolProxy::END, KisTool::Primary, event, event);
d->active = false;
}
KisAbstractInputAction::end(event);
}
void KisToolInvocationAction::inputEvent(QEvent* event)
{
if (!d->active) return;
inputManager()->toolProxy()->
- forwardEvent(KisToolProxy::CONTINUE, KisTool::Primary, event, event,
- inputManager()->lastTabletEvent());
+ forwardEvent(KisToolProxy::CONTINUE, KisTool::Primary, event, event);
}
void KisToolInvocationAction::processUnhandledEvent(QEvent* event)
{
bool savedState = d->active;
d->active = true;
inputEvent(event);
d->active = savedState;
}
bool KisToolInvocationAction::supportsHiResInputEvents() const
{
return inputManager()->toolProxy()->primaryActionSupportsHiResEvents();
}
bool KisToolInvocationAction::isShortcutRequired(int shortcut) const
{
//These really all are pretty important for basic user interaction.
Q_UNUSED(shortcut)
return true;
}
diff --git a/krita/ui/input/kis_zoom_action.cpp b/krita/ui/input/kis_zoom_action.cpp
index ee4b69dcabd..d1a8b7411ae 100644
--- a/krita/ui/input/kis_zoom_action.cpp
+++ b/krita/ui/input/kis_zoom_action.cpp
@@ -1,257 +1,246 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_zoom_action.h"
#include <QApplication>
#include <klocale.h>
#include <KoCanvasControllerWidget.h>
#include <KoZoomController.h>
#include <kis_canvas2.h>
#include <kis_canvas_controller.h>
#include "kis_cursor.h"
#include "KisViewManager.h"
#include "kis_input_manager.h"
#include "kis_config.h"
class KisZoomAction::Private
{
public:
Private(KisZoomAction *qq) : q(qq), distance(0), lastDistance(0.f) {}
QPointF centerPoint(QTouchEvent* event);
KisZoomAction *q;
int distance;
Shortcuts mode;
QPointF lastPosition;
float lastDistance;
void zoomTo(bool zoomIn, QEvent *event);
};
QPointF KisZoomAction::Private::centerPoint(QTouchEvent* event)
{
QPointF result;
int count = 0;
foreach (QTouchEvent::TouchPoint point, event->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
result += point.screenPos();
count++;
}
}
if (count > 0) {
return result / count;
} else {
return QPointF();
}
}
void KisZoomAction::Private::zoomTo(bool zoomIn, QEvent *event)
{
KoZoomAction *zoomAction = q->inputManager()->canvas()->viewManager()->zoomController()->zoomAction();
if (event) {
- QPoint pos;
- QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
- if (mouseEvent) {
- pos = mouseEvent->pos();
- } else {
- QWheelEvent *wheelEvent = dynamic_cast<QWheelEvent*>(event);
- if (wheelEvent) {
- pos = wheelEvent->pos();
- } else {
- qWarning() << "Unhandled type of event";
- }
- }
+ QPoint pos = eventPos(event);
float oldZoom = zoomAction->effectiveZoom();
float newZoom = zoomIn ?
zoomAction->nextZoomLevel() : zoomAction->prevZoomLevel();
KoCanvasControllerWidget *controller =
dynamic_cast<KoCanvasControllerWidget*>(
q->inputManager()->canvas()->canvasController());
controller->zoomRelativeToPoint(pos, newZoom / oldZoom);
} else {
if (zoomIn) {
zoomAction->zoomIn();
} else {
zoomAction->zoomOut();
}
}
}
KisZoomAction::KisZoomAction()
: KisAbstractInputAction("Zoom Canvas")
, d(new Private(this))
{
setName(i18n("Zoom Canvas"));
setDescription(i18n("The <i>Zoom Canvas</i> action zooms the canvas."));
QHash< QString, int > shortcuts;
shortcuts.insert(i18n("Zoom Mode"), ZoomModeShortcut);
shortcuts.insert(i18n("Discrete Zoom Mode"), DiscreteZoomModeShortcut);
shortcuts.insert(i18n("Zoom In"), ZoomInShortcut);
shortcuts.insert(i18n("Zoom Out"), ZoomOutShortcut);
shortcuts.insert(i18n("Reset Zoom to 100%"), ZoomResetShortcut);
shortcuts.insert(i18n("Fit to Page"), ZoomToPageShortcut);
shortcuts.insert(i18n("Fit to Width"), ZoomToWidthShortcut);
setShortcutIndexes(shortcuts);
}
KisZoomAction::~KisZoomAction()
{
delete d;
}
int KisZoomAction::priority() const
{
return 4;
}
void KisZoomAction::activate(int shortcut)
{
if (shortcut == DiscreteZoomModeShortcut) {
QApplication::setOverrideCursor(KisCursor::zoomDiscreteCursor());
} else /* if (shortcut == SmoothZoomModeShortcut) */ {
QApplication::setOverrideCursor(KisCursor::zoomSmoothCursor());
}
}
void KisZoomAction::deactivate(int shortcut)
{
Q_UNUSED(shortcut);
QApplication::restoreOverrideCursor();
}
void KisZoomAction::begin(int shortcut, QEvent *event)
{
KisAbstractInputAction::begin(shortcut, event);
d->lastDistance = 0.f;
switch(shortcut) {
case ZoomModeShortcut: {
d->mode = (Shortcuts)shortcut;
QTouchEvent *tevent = dynamic_cast<QTouchEvent*>(event);
if(tevent)
d->lastPosition = d->centerPoint(tevent);
break;
}
case DiscreteZoomModeShortcut:
d->mode = (Shortcuts)shortcut;
d->distance = 0;
break;
case ZoomInShortcut:
d->zoomTo(true, event);
break;
case ZoomOutShortcut:
d->zoomTo(false, event);
break;
case ZoomResetShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
break;
case ZoomToPageShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_PAGE, 1.0);
break;
case ZoomToWidthShortcut:
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_WIDTH, 1.0);
break;
}
}
void KisZoomAction::inputEvent( QEvent* event )
{
switch (event->type()) {
case QEvent::TouchUpdate: {
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
QPointF center = d->centerPoint(tevent);
int count = 0;
float dist = 0.0f;
foreach(const QTouchEvent::TouchPoint &point, tevent->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
count++;
dist += (point.screenPos() - center).manhattanLength();
}
}
dist /= count;
float delta = qFuzzyCompare(1.0f, 1.0f + d->lastDistance) ? 1.f : dist / d->lastDistance;
if(qAbs(delta) > 0.1f) {
qreal zoom = inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
Q_UNUSED(zoom);
static_cast<KisCanvasController*>(inputManager()->canvas()->canvasController())->zoomRelativeToPoint(center.toPoint(), delta);
d->lastDistance = dist;
// Also do panning here, as doing it later requires a further check for validity
QPointF moveDelta = center - d->lastPosition;
inputManager()->canvas()->canvasController()->pan(-moveDelta.toPoint());
d->lastPosition = center;
}
}
default:
break;
}
KisAbstractInputAction::inputEvent(event);
}
-void KisZoomAction::mouseMoved(const QPointF &lastPos, const QPointF &pos)
+void KisZoomAction::cursorMoved(const QPointF &lastPos, const QPointF &pos)
{
QPointF diff = -(pos - lastPos);
const int stepCont = 100;
const int stepDisc = 20;
if (d->mode == ZoomModeShortcut) {
KisConfig cfg;
float coeff;
if (cfg.readEntry<bool>("InvertMiddleClickZoom", false)) {
coeff = 1.0 - qreal(diff.y()) / stepCont;
}
else {
coeff = 1.0 + qreal(diff.y()) / stepCont;
}
float zoom = coeff * inputManager()->canvas()->viewManager()->zoomController()->zoomAction()->effectiveZoom();
inputManager()->canvas()->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom);
} else if (d->mode == DiscreteZoomModeShortcut) {
d->distance += diff.y();
bool zoomIn = d->distance > 0;
while (qAbs(d->distance) > stepDisc) {
d->zoomTo(zoomIn, 0);
d->distance += zoomIn ? -stepDisc : stepDisc;
}
}
}
bool KisZoomAction::isShortcutRequired(int shortcut) const
{
return shortcut == ZoomModeShortcut;
}
diff --git a/krita/ui/input/kis_zoom_action.h b/krita/ui/input/kis_zoom_action.h
index 9baa9ecbfe7..67bf0337828 100644
--- a/krita/ui/input/kis_zoom_action.h
+++ b/krita/ui/input/kis_zoom_action.h
@@ -1,63 +1,63 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ZOOM_ACTION_H
#define KIS_ZOOM_ACTION_H
#include "kis_abstract_input_action.h"
/**
* \brief Zoom Canvas implementation of KisAbstractInputAction.
*
* The Zoom Canvas action zooms the canvas.
*/
class KisZoomAction : public KisAbstractInputAction
{
public:
/**
* The different behaviours for this action.
*/
enum Shortcuts {
ZoomModeShortcut, ///< Toggle zoom mode.
DiscreteZoomModeShortcut, ///< Toggle discrete zoom mode
ZoomInShortcut, ///< Zoom in by a fixed amount.
ZoomOutShortcut, ///< Zoom out by a fixed amount.
ZoomResetShortcut, ///< Reset zoom to 100%.
ZoomToPageShortcut, ///< Zoom fit to page.
ZoomToWidthShortcut ///< Zoom fit to width.
};
explicit KisZoomAction();
virtual ~KisZoomAction();
virtual int priority() const;
void activate(int shortcut);
void deactivate(int shortcut);
void begin(int shortcut, QEvent *event = 0);
void inputEvent(QEvent* event);
- void mouseMoved(const QPointF &lastPos, const QPointF &pos);
+ void cursorMoved(const QPointF &lastPos, const QPointF &pos);
virtual bool isShortcutRequired(int shortcut) const;
private:
class Private;
Private * const d;
};
#endif // KIS_ZOOM_ACTION_H
diff --git a/krita/ui/input/wintab/kis_tablet_event_rate_logger.h b/krita/ui/input/wintab/kis_tablet_event_rate_logger.h
index 25274c57d56..a58f804a783 100644
--- a/krita/ui/input/wintab/kis_tablet_event_rate_logger.h
+++ b/krita/ui/input/wintab/kis_tablet_event_rate_logger.h
@@ -1,84 +1,84 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TABLET_EVENT_RATE_LOGGER_H
#define __KIS_TABLET_EVENT_RATE_LOGGER_H
#include "kis_algebra_2d.h"
class KisTabletEventRateLogger
{
public:
KisTabletEventRateLogger(int period, const QString &id)
: m_period(period),
m_numSamples(0),
m_avgTime(0),
m_id(id)
{
}
void logTabletEvent(KisTabletEvent *tevent) {
QPointF newPos = tevent->hiResGlobalPos();
if (!m_numSamples) {
m_numSamples++;
m_time.start();
m_lastPos = newPos;
return;
}
qreal oldCoeff = m_numSamples;
qreal newCoeff = ++m_numSamples;
m_avgTime = (m_avgTime * oldCoeff + m_time.restart()) / newCoeff;
QPointF posDiff = KisAlgebra2D::abs(newPos - m_lastPos);
m_avgPosDiff = (m_avgPosDiff * oldCoeff + posDiff) / newCoeff;
m_lastPos = newPos;
if (m_numSamples % m_period == 0) {
dumpValues();
}
}
void dumpValues() {
- qDebug() << "########################";
- qDebug() << ppVar(m_id);
- qDebug() << ppVar(m_avgTime);
- qDebug() << ppVar(m_avgPosDiff);
- qDebug() << "spd:" << m_avgPosDiff / m_avgTime;
- qDebug() << "########################";
+ dbgKrita << "########################";
+ dbgKrita << ppVar(m_id);
+ dbgKrita << ppVar(m_avgTime);
+ dbgKrita << ppVar(m_avgPosDiff);
+ dbgKrita << "spd:" << m_avgPosDiff / m_avgTime;
+ dbgKrita << "########################";
}
private:
int m_period;
int m_numSamples;
qreal m_avgTime;
QPointF m_avgPosDiff;
QPointF m_lastPos;
QTime m_time;
QString m_id;
};
#endif /* __KIS_TABLET_EVENT_RATE_LOGGER_H */
diff --git a/krita/ui/input/wintab/kis_tablet_support.h b/krita/ui/input/wintab/kis_tablet_support.h
index 501601fc16b..042885faf35 100644
--- a/krita/ui/input/wintab/kis_tablet_support.h
+++ b/krita/ui/input/wintab/kis_tablet_support.h
@@ -1,225 +1,225 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TABLET_SUPPORT_H
#define __KIS_TABLET_SUPPORT_H
#include <QtGlobal>
#include <QLibrary>
#include <QPointer>
#include <QPointF>
#include <QVector>
#include <QList>
#include <QMap>
#include <QTabletEvent>
#ifdef HAVE_X11
#include "kis_config.h"
#include <algorithm>
#include <X11/extensions/XInput.h>
#include <kis_global.h>
#include "kis_incremental_average.h"
#endif
struct QTabletDeviceData
{
#ifndef Q_WS_MAC
int minPressure;
int maxPressure;
int minTanPressure;
int maxTanPressure;
int minX, maxX, minY, maxY, minZ, maxZ;
int sysOrgX, sysOrgY, sysExtX, sysExtY;
#ifdef HAVE_X11 // on windows the scale is fixed (and hardcoded)
int minRotation;
int maxRotation;
#endif /* HAVE_X11 */
inline QPointF scaleCoord(int coordX, int coordY, int outOriginX, int outExtentX,
int outOriginY, int outExtentY) const;
#endif
#if defined(HAVE_X11) || (defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA))
QPointer<QWidget> widgetToGetPress;
#endif
#ifdef HAVE_X11
int deviceType;
enum {
TOTAL_XINPUT_EVENTS = 64
};
void *device;
int eventCount;
long unsigned int eventList[TOTAL_XINPUT_EVENTS]; // XEventClass is in fact a long unsigned int
int xinput_motion;
int xinput_key_press;
int xinput_key_release;
int xinput_button_press;
int xinput_button_release;
int xinput_proximity_in;
int xinput_proximity_out;
#elif defined(Q_WS_MAC)
quint64 tabletUniqueID;
int tabletDeviceType;
int tabletPointerType;
int capabilityMask;
#endif
// Added by Krita
#if defined(Q_OS_MAC) || defined(Q_OS_WIN32)
QMap<quint8, quint8> buttonsMap;
qint64 llId;
int currentPointerType;
int currentDevice;
#endif
#ifdef HAVE_X11
bool isTouchWacomTablet;
/**
* Different tablets have different assignment of axes reported by
* the XInput subsystem. More than that some of the drivers demand
* local storage of the tablet axes' state, because in the events
* they report only recently changed axes. SavedAxesData was
* created to handle all these complexities.
*/
class SavedAxesData {
public:
enum AxesIndexes {
XCoord = 0,
YCoord,
Pressure,
XTilt,
YTilt,
Rotation,
Unused,
NAxes
};
public:
SavedAxesData()
: m_workaroundX11SmoothPressureSteps(KisConfig().workaroundX11SmoothPressureSteps()),
m_pressureAverage(m_workaroundX11SmoothPressureSteps)
{
for (int i = 0; i < NAxes; i++) {
m_x11_to_local_axis_mapping.append((AxesIndexes)i);
}
if (m_workaroundX11SmoothPressureSteps) {
- qWarning() << "WARNING: Workaround for broken tablet"
+ warnKrita << "WARNING: Workaround for broken tablet"
<< "pressure reports is activated. Number"
<< "of smooth steps:"
<< m_workaroundX11SmoothPressureSteps;
}
}
void tryFetchAxesMapping(XDevice *dev);
void setAxesMap(const QVector<AxesIndexes> &axesMap) {
// the size of \p axesMap can be smaller/equal/bigger
// than m_axes_data. Everything depends on the driver
m_x11_to_local_axis_mapping = axesMap;
}
inline QPointF position(const QTabletDeviceData *tablet, const QRect &screenArea) const {
return tablet->scaleCoord(m_axis_data[XCoord], m_axis_data[YCoord],
screenArea.x(), screenArea.width(),
screenArea.y(), screenArea.height());
}
inline int pressure() const {
return m_axis_data[Pressure];
}
inline int xTilt() const {
return m_axis_data[XTilt];
}
inline int yTilt() const {
return m_axis_data[YTilt];
}
inline int rotation() const {
return m_axis_data[Rotation];
}
bool updateAxesData(int firstAxis, int axesCount, const int *axes) {
for (int srcIt = 0, dstIt = firstAxis;
srcIt < axesCount;
srcIt++, dstIt++) {
int index = m_x11_to_local_axis_mapping[dstIt];
int newValue = axes[srcIt];
if (m_workaroundX11SmoothPressureSteps > 0 &&
index == Pressure) {
newValue = m_pressureAverage.pushThrough(newValue);
}
m_axis_data[index] = newValue;
}
return true;
}
private:
int m_axis_data[NAxes];
QVector<AxesIndexes> m_x11_to_local_axis_mapping;
int m_workaroundX11SmoothPressureSteps;
KisIncrementalAverage m_pressureAverage;
};
SavedAxesData savedAxesData;
#endif /* HAVE_X11 */
};
static inline int sign(int x)
{
return x >= 0 ? 1 : -1;
}
#ifndef Q_WS_MAC
inline QPointF QTabletDeviceData::scaleCoord(int coordX, int coordY,
int outOriginX, int outExtentX,
int outOriginY, int outExtentY) const
{
QPointF ret;
if (sign(outExtentX) == sign(maxX))
ret.setX(((coordX - minX) * qAbs(outExtentX) / qAbs(qreal(maxX - minX))) + outOriginX);
else
ret.setX(((qAbs(maxX) - (coordX - minX)) * qAbs(outExtentX) / qAbs(qreal(maxX - minX)))
+ outOriginX);
if (sign(outExtentY) == sign(maxY))
ret.setY(((coordY - minY) * qAbs(outExtentY) / qAbs(qreal(maxY - minY))) + outOriginY);
else
ret.setY(((qAbs(maxY) - (coordY - minY)) * qAbs(outExtentY) / qAbs(qreal(maxY - minY)))
+ outOriginY);
return ret;
}
#endif
typedef QList<QTabletDeviceData> QTabletDeviceDataList;
QTabletDeviceDataList *qt_tablet_devices();
#endif /* __KIS_TABLET_SUPPORT_H */
diff --git a/krita/ui/input/wintab/kis_tablet_support_win.cpp b/krita/ui/input/wintab/kis_tablet_support_win.cpp
index 215fe14181d..d206453c7bb 100644
--- a/krita/ui/input/wintab/kis_tablet_support_win.cpp
+++ b/krita/ui/input/wintab/kis_tablet_support_win.cpp
@@ -1,760 +1,760 @@
/*
* Copyright (c) 2013 Digia Plc and/or its subsidiary(-ies).
* Copyright (c) 2013 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <input/kis_tablet_event.h>
#include "kis_tablet_support_win.h"
#include "kis_tablet_support.h"
#include <kis_debug.h>
#include <QApplication>
#include <QDesktopWidget>
#include <math.h>
#define Q_PI M_PI
#include <input/kis_extended_modifiers_mapper.h>
#include <input/kis_tablet_debugger.h>
// For "inline tool switches"
#include <KoToolManager.h>
#include <KoInputDevice.h>
#include "kis_screen_size_choice_dialog.h"
/**
* A set of definitions to form a structure of a WinTab packet. This
* structure must be 100% the same as the structure of the packet
* defined by Qt internally
*/
#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE \
| PK_ORIENTATION | PK_CURSOR | PK_Z)
#define PACKETMODE 0
#include "wintab.h"
#ifndef CSR_TYPE
#define CSR_TYPE 20 // Some old Wacom wintab.h may not provide this constant.
#endif
#include "pktdef.h"
/**
* Pointers to the API functions resolved manually
*/
typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID);
typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID);
typedef int (API *PtrWTPacketsPeek)(HCTX, int, LPVOID);
typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT);
typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL);
/**
* Definitions at http://www.wacomeng.com/windows/docs/Wintab_v140.htm
*/
static PtrWTInfo ptrWTInfo = 0;
static PtrWTPacketsGet ptrWTPacketsGet = 0;
static PtrWTPacketsPeek ptrWTPacketsPeek = 0;
static PtrWTGet ptrWTGet = 0;
static PtrWTOverlap ptrWTOverlap = 0;
/**
* A cached array for fetching packets from the WinTab queue
*/
enum { QT_TABLET_NPACKETQSIZE = 128 };
static PACKET globalPacketBuf[QT_TABLET_NPACKETQSIZE]; // our own tablet packet queue.
/**
* Variables to handle the Tablet Context
*/
static HCTX qt_tablet_context = 0;
static bool qt_tablet_tilt_support;
/**
* The widget that got the latest tablet press.
*
* It should be impossible that one widget would get tablet press and
* the other tablet release. Keeping track of which widget got the
* press event allows to ensure that exactly the same widget would get
* the release.
*/
static QPointer<QWidget> kis_tablet_pressed = 0;
/**
* The hash taple of available cursor, containing information about
* each curror, its resolution and capabilities
*/
typedef QHash<quint64, QTabletDeviceData> QTabletCursorInfo;
Q_GLOBAL_STATIC(QTabletCursorInfo, tCursorInfo)
static QTabletDeviceData currentTabletPointer;
/**
* This is an inelegant solution to record pen / eraser switches.
* On the Surface Pro 3 we are only notified of cursor changes at the last minute.
* The recommended way to handle switches is WT_CSRCHANGE, but that doesn't work
* unless we save packet ID information, and we cannot change the structure of the
* PACKETDATA due to Qt restrictions.
*
* Furthermore, WT_CSRCHANGE only ever appears *after* we receive the packet.
*/
static UINT currentCursor = 0;
static bool dialogOpen = false; //< KisTabletSupportWin is not a Q_OBJECT and can't accept dialog signals
static bool inlineSwitching = false; //< Only enable this on SP3 or other devices with the same issue.
/**
* This is a default implementation of a class for converting the
* WinTab value of the buttons pressed to the Qt buttons. This class
* may be substituted from the UI.
*/
struct DefaultButtonsConverter : public KisTabletSupportWin::ButtonsConverter
{
void convert(DWORD btnOld, DWORD btnNew,
Qt::MouseButton *button,
Qt::MouseButtons *buttons) {
int pressedButtonValue = btnNew ^ btnOld;
*button = buttonValueToEnum(pressedButtonValue);
*buttons = Qt::NoButton;
for (int i = 0; i < 3; i++) {
int btn = 0x1 << i;
if (btn & btnNew) {
Qt::MouseButton convertedButton =
buttonValueToEnum(btn);
*buttons |= convertedButton;
/**
* If a button that is present in hardware input is
* mapped to a Qt::NoButton, it means that it is going
* to be eaten by the driver, for example by its
* "Pan/Scroll" feature. Therefore we shouldn't handle
* any of the events associated to it. So just return
* Qt::NoButton here.
*/
if (convertedButton == Qt::NoButton) {
*button = Qt::NoButton;
*buttons = Qt::NoButton;
break;
}
}
}
}
private:
Qt::MouseButton buttonValueToEnum(DWORD button) {
const int leftButtonValue = 0x1;
const int middleButtonValue = 0x2;
const int rightButtonValue = 0x4;
const int doubleClickButtonValue = 0x7;
button = currentTabletPointer.buttonsMap.value(button);
return button == leftButtonValue ? Qt::LeftButton :
button == rightButtonValue ? Qt::RightButton :
button == doubleClickButtonValue ? Qt::MiddleButton :
button == middleButtonValue ? Qt::MiddleButton :
button ? Qt::LeftButton /* fallback item */ :
Qt::NoButton;
}
};
static KisTabletSupportWin::ButtonsConverter *globalButtonsConverter =
new DefaultButtonsConverter();
/**
* Resolves the WINTAB api functions
*/
static void initWinTabFunctions()
{
QLibrary library(QLatin1String("wintab32"));
ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW");
Q_ASSERT(ptrWTInfo);
ptrWTGet = (PtrWTGet)library.resolve("WTGetW");
ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet");
ptrWTPacketsPeek = (PtrWTPacketsGet)library.resolve("WTPacketsPeek");
ptrWTOverlap = (PtrWTOverlap)library.resolve("WTOverlap");
}
void printContext(const LOGCONTEXT &lc)
{
- qDebug() << "Context data:";
- qDebug() << ppVar(lc.lcName);
- qDebug() << ppVar(lc.lcDevice);
- qDebug() << ppVar(lc.lcInOrgX);
- qDebug() << ppVar(lc.lcInOrgY);
- qDebug() << ppVar(lc.lcInExtX);
- qDebug() << ppVar(lc.lcInExtY);
- qDebug() << ppVar(lc.lcOutOrgX);
- qDebug() << ppVar(lc.lcOutOrgY);
- qDebug() << ppVar(lc.lcOutExtX);
- qDebug() << ppVar(lc.lcOutExtY);
- qDebug() << ppVar(lc.lcSysOrgX);
- qDebug() << ppVar(lc.lcSysOrgY);
- qDebug() << ppVar(lc.lcSysExtX);
- qDebug() << ppVar(lc.lcSysExtY);
-
- qDebug() << "Qt Desktop Geometry" << QApplication::desktop()->geometry();
+ dbgKrita << "Context data:";
+ dbgKrita << ppVar(lc.lcName);
+ dbgKrita << ppVar(lc.lcDevice);
+ dbgKrita << ppVar(lc.lcInOrgX);
+ dbgKrita << ppVar(lc.lcInOrgY);
+ dbgKrita << ppVar(lc.lcInExtX);
+ dbgKrita << ppVar(lc.lcInExtY);
+ dbgKrita << ppVar(lc.lcOutOrgX);
+ dbgKrita << ppVar(lc.lcOutOrgY);
+ dbgKrita << ppVar(lc.lcOutExtX);
+ dbgKrita << ppVar(lc.lcOutExtY);
+ dbgKrita << ppVar(lc.lcSysOrgX);
+ dbgKrita << ppVar(lc.lcSysOrgY);
+ dbgKrita << ppVar(lc.lcSysExtX);
+ dbgKrita << ppVar(lc.lcSysExtY);
+
+ dbgKrita << "Qt Desktop Geometry" << QApplication::desktop()->geometry();
}
/**
* Initializes the QTabletDeviceData structure for \p uniqueId cursor
* and stores it in the global cache
*/
static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab)
{
Q_ASSERT(ptrWTInfo);
Q_ASSERT(ptrWTGet);
Q_ASSERT(!tCursorInfo()->contains(uniqueId));
if (dialogOpen) {
return;
}
/* browse WinTab's many info items to discover pressure handling. */
AXIS np;
LOGCONTEXT lc;
/* get the current context for its device variable. */
ptrWTGet(hTab, &lc);
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "# Getting current context:";
+ dbgKrita << "# Getting current context:";
printContext(lc);
}
/* get the size of the pressure axis. */
QTabletDeviceData tdd;
tdd.llId = uniqueId;
ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &np);
tdd.minPressure = int(np.axMin);
tdd.maxPressure = int(np.axMax);
ptrWTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &np);
tdd.minTanPressure = int(np.axMin);
tdd.maxTanPressure = int(np.axMax);
// some tablets don't support tilt, check if it is possible,
struct tagAXIS tpOri[3];
qt_tablet_tilt_support = ptrWTInfo(WTI_DEVICES, DVC_ORIENTATION, &tpOri);
if (qt_tablet_tilt_support) {
// check for azimuth and altitude
qt_tablet_tilt_support = tpOri[0].axResolution && tpOri[1].axResolution;
}
tdd.minX = int(lc.lcOutOrgX);
tdd.maxX = int(qAbs(lc.lcOutExtX)) + int(lc.lcOutOrgX);
tdd.minY = int(lc.lcOutOrgY);
tdd.maxY = int(qAbs(lc.lcOutExtY)) + int(lc.lcOutOrgY);
tdd.minZ = int(lc.lcOutOrgZ);
tdd.maxZ = int(qAbs(lc.lcOutExtZ)) + int(lc.lcOutOrgZ);
QRect qtDesktopRect = QApplication::desktop()->geometry();
QRect wintabDesktopRect(lc.lcSysOrgX, lc.lcSysOrgY,
lc.lcSysExtX, lc.lcSysExtY);
- qDebug() << ppVar(qtDesktopRect);
- qDebug() << ppVar(wintabDesktopRect);
+ dbgKrita << ppVar(qtDesktopRect);
+ dbgKrita << ppVar(wintabDesktopRect);
QRect desktopRect = wintabDesktopRect;
{
KisScreenSizeChoiceDialog dlg(0,
wintabDesktopRect,
qtDesktopRect);
KisExtendedModifiersMapper mapper;
KisExtendedModifiersMapper::ExtendedModifiers modifiers =
mapper.queryExtendedModifiers();
if (modifiers.contains(Qt::Key_Shift) ||
(!dlg.canUseDefaultSettings() &&
qtDesktopRect != wintabDesktopRect)) {
dialogOpen = true;
dlg.exec();
}
desktopRect = dlg.screenRect();
dialogOpen = false;
}
tdd.sysOrgX = desktopRect.x();
tdd.sysOrgY = desktopRect.y();
tdd.sysExtX = desktopRect.width();
tdd.sysExtY = desktopRect.height();
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "# Axes configuration";
- qDebug() << ppVar(tdd.minPressure) << ppVar(tdd.maxPressure);
- qDebug() << ppVar(tdd.minTanPressure) << ppVar(tdd.maxTanPressure);
- qDebug() << ppVar(qt_tablet_tilt_support);
- qDebug() << ppVar(tdd.minX) << ppVar(tdd.maxX);
- qDebug() << ppVar(tdd.minY) << ppVar(tdd.maxY);
- qDebug() << ppVar(tdd.minZ) << ppVar(tdd.maxZ);
- qDebug().nospace() << "# csr type: 0x" << hex << csr_type;
+ dbgKrita << "# Axes configuration";
+ dbgKrita << ppVar(tdd.minPressure) << ppVar(tdd.maxPressure);
+ dbgKrita << ppVar(tdd.minTanPressure) << ppVar(tdd.maxTanPressure);
+ dbgKrita << ppVar(qt_tablet_tilt_support);
+ dbgKrita << ppVar(tdd.minX) << ppVar(tdd.maxX);
+ dbgKrita << ppVar(tdd.minY) << ppVar(tdd.maxY);
+ dbgKrita << ppVar(tdd.minZ) << ppVar(tdd.maxZ);
+ dbgKrita.nospace() << "# csr type: 0x" << hex << csr_type;
LOGCONTEXT lcMine;
/* get default region */
ptrWTInfo(WTI_DEFCONTEXT, 0, &lcMine);
- qDebug() << "# Getting default context:";
+ dbgKrita << "# Getting default context:";
printContext(lcMine);
}
const uint cursorTypeBitMask = 0x0F06; // bitmask to find the specific cursor type (see Wacom FAQ)
if (((csr_type & 0x0006) == 0x0002) && ((csr_type & cursorTypeBitMask) != 0x0902)) {
tdd.currentDevice = QTabletEvent::Stylus;
} else if (csr_type == 0x4020) { // Surface Pro 2 tablet device
tdd.currentDevice = QTabletEvent::Stylus;
} else {
switch (csr_type & cursorTypeBitMask) {
case 0x0802:
tdd.currentDevice = QTabletEvent::Stylus;
break;
case 0x0902:
tdd.currentDevice = QTabletEvent::Airbrush;
break;
case 0x0004:
tdd.currentDevice = QTabletEvent::FourDMouse;
break;
case 0x0006:
tdd.currentDevice = QTabletEvent::Puck;
break;
case 0x0804:
tdd.currentDevice = QTabletEvent::RotationStylus;
break;
default:
tdd.currentDevice = QTabletEvent::NoDevice;
}
}
tCursorInfo()->insert(uniqueId, tdd);
}
/**
* Updates the current cursor of the stylus
*/
static void tabletChangeCursor(QTabletDeviceData &tdd, const UINT newCursor)
{
switch (newCursor % 3) { // %3 for dual track
case 0:
tdd.currentPointerType = QTabletEvent::Cursor;
break;
case 1:
tdd.currentPointerType = QTabletEvent::Pen;
break;
case 2:
tdd.currentPointerType = QTabletEvent::Eraser;
break;
default:
tdd.currentPointerType = QTabletEvent::UnknownPointer;
}
- qDebug() << "WinTab: updating cursor type" << ppVar(newCursor);
+ dbgKrita << "WinTab: updating cursor type" << ppVar(newCursor);
currentCursor = newCursor;
}
/**
* Logic to handle a tablet device entering proximity or when switching tools.
*/
static void tabletUpdateCursor(const UINT newCursor)
{
UINT csr_physid;
ptrWTInfo(WTI_CURSORS + newCursor, CSR_PHYSID, &csr_physid);
UINT csr_type;
ptrWTInfo(WTI_CURSORS + newCursor, CSR_TYPE, &csr_type);
const UINT deviceIdMask = 0xFF6; // device type mask && device color mask
quint64 uniqueId = (csr_type & deviceIdMask);
uniqueId = (uniqueId << 32) | csr_physid;
const QTabletCursorInfo *const globalCursorInfo = tCursorInfo();
bool isInit = !globalCursorInfo->contains(uniqueId);
if (isInit) {
tabletInit(uniqueId, csr_type, qt_tablet_context);
}
// Check tablet name to enable Surface Pro 3 workaround.
#ifdef UNICODE
UINT nameLength = ptrWTInfo(WTI_DEVICES, DVC_NAME, 0);
TCHAR* dvcName = new TCHAR[nameLength + 1];
ptrWTInfo(WTI_DEVICES, DVC_NAME, dvcName);
QString qDvcName = QString::fromWCharArray((const wchar_t*)dvcName);
delete dvcName;
- qDebug() << "DVC_NAME =" << qDvcName;
+ dbgKrita << "DVC_NAME =" << qDvcName;
if (qDvcName == QString::fromLatin1("N-trig DuoSense device")) {
inlineSwitching = true;
} else {
inlineSwitching = false;
}
#endif
currentTabletPointer = globalCursorInfo->value(uniqueId);
tabletChangeCursor(currentTabletPointer, newCursor);
BYTE logicalButtons[32];
memset(logicalButtons, 0, 32);
ptrWTInfo(WTI_CURSORS + newCursor, CSR_SYSBTNMAP, &logicalButtons);
currentTabletPointer.buttonsMap[0x1] = logicalButtons[0];
currentTabletPointer.buttonsMap[0x2] = logicalButtons[1];
currentTabletPointer.buttonsMap[0x4] = logicalButtons[2];
if (isInit && KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "--------------------------";
- qDebug() << "--- Tablet buttons map ---";
+ dbgKrita << "--------------------------";
+ dbgKrita << "--- Tablet buttons map ---";
for (int i = 0; i < 16; i++) {
- qDebug() << "( 1 <<" << 2*i << ")" << "->" << logicalButtons[2*i]
+ dbgKrita << "( 1 <<" << 2*i << ")" << "->" << logicalButtons[2*i]
<< "( 1 <<" << 2*i+1 << ")" << "->" << logicalButtons[2*i+1];
}
- qDebug() << "--------------------------";
+ dbgKrita << "--------------------------";
}
}
/**
* Qt generates spoofed mouse events for certain rejected tablet events.
* When those mouse events are unnecessary we catch and remove them
* with this event filter.
*/
class EventEater : public QObject {
public:
EventEater(QObject *p) : QObject(p), m_eventType(QEvent::None) {}
bool eventFilter(QObject* object, QEvent* event ) {
if (event->type() == m_eventType) {
m_eventType = QEvent::None;
return true;
}
return QObject::eventFilter(object, event);
}
void pleaseEatNextEvent(QEvent::Type eventType) {
m_eventType = eventType;
}
private:
QEvent::Type m_eventType;
};
static EventEater *globalEventEater = 0;
bool translateTabletEvent(const MSG &msg, PACKET *localPacketBuf,
int numPackets)
{
Q_UNUSED(msg);
POINT ptNew;
static DWORD btnNew, btnOld, btnChange;
qreal prsNew;
ORIENTATION ort;
int i,
tiltX,
tiltY;
bool sendEvent = false;
KisTabletEvent::ExtraEventType t;
int z = 0;
qreal rotation = 0.0;
qreal tangentialPressure;
/**
* There is a bug in Qt (tested on 4.8.5) and if you press
* Win+UpKey hotkey right after the start of the application, the
* next call to QApplication::keyboardModifiers() will return Alt
* key pressed, although it isn't. That is why we do not rely on
* keyboardModifiers(), but just query the keys state directly.
*/
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
for (i = 0; i < numPackets; i++) {
btnOld = btnNew;
btnNew = localPacketBuf[i].pkButtons;
btnChange = btnOld ^ btnNew;
bool buttonPressed = btnChange && btnNew > btnOld;
bool buttonReleased = btnChange && btnNew < btnOld;
bool anyButtonsStillPressed = btnNew;
ptNew.x = UINT(localPacketBuf[i].pkX);
ptNew.y = UINT(localPacketBuf[i].pkY);
z = UINT(localPacketBuf[i].pkZ);
prsNew = 0.0;
QPointF hiResGlobal = currentTabletPointer.scaleCoord(ptNew.x, ptNew.y,
currentTabletPointer.sysOrgX, currentTabletPointer.sysExtX,
currentTabletPointer.sysOrgY, currentTabletPointer.sysExtY);
if (KisTabletDebugger::instance()->debugRawTabletValues()) {
- qDebug() << "WinTab (RC):"
+ dbgKrita << "WinTab (RC):"
<< "Dsk:" << QRect(currentTabletPointer.sysOrgX, currentTabletPointer.sysOrgY, currentTabletPointer.sysExtX, currentTabletPointer.sysExtY)
<< "Raw:" << ptNew.x << ptNew.y
<< "Scaled:" << hiResGlobal;
- qDebug() << "WinTab (BN):"
+ dbgKrita << "WinTab (BN):"
<< "old:" << btnOld
<< "new:" << btnNew
<< "diff:" << (btnOld ^ btnNew)
<< (buttonPressed ? "P" : buttonReleased ? "R" : ".");
}
Qt::MouseButton button = Qt::NoButton;
Qt::MouseButtons buttons;
globalButtonsConverter->convert(btnOld, btnNew, &button, &buttons);
t = KisTabletEvent::TabletMoveEx;
if (buttonPressed && button != Qt::NoButton) {
t = KisTabletEvent::TabletPressEx;
} else if (buttonReleased && button != Qt::NoButton) {
t = KisTabletEvent::TabletReleaseEx;
}
if (anyButtonsStillPressed) {
if (currentTabletPointer.currentPointerType == QTabletEvent::Pen || currentTabletPointer.currentPointerType == QTabletEvent::Eraser)
prsNew = localPacketBuf[i].pkNormalPressure
/ qreal(currentTabletPointer.maxPressure
- currentTabletPointer.minPressure);
else
prsNew = 0;
}
QPoint globalPos(qRound(hiResGlobal.x()), qRound(hiResGlobal.y()));
// make sure the tablet event get's sent to the proper widget...
QWidget *w = 0;
/**
* Find the appropriate window in an order of preference
*/
if (!w) w = qApp->widgetAt(globalPos);
if (!w) w = qApp->activeWindow();
QWidget *parentOverride = 0;
if (!parentOverride) parentOverride = qApp->activePopupWidget();
if (!parentOverride) parentOverride = qApp->activeModalWidget();
if (!w || (parentOverride && !parentOverride->isAncestorOf(w))) {
w = parentOverride;
}
if (kis_tablet_pressed) {
w = kis_tablet_pressed;
}
if (t == KisTabletEvent::TabletPressEx && !kis_tablet_pressed) {
kis_tablet_pressed = w;
}
if (!anyButtonsStillPressed) {
kis_tablet_pressed = 0;
}
QPoint localPos = w->mapFromGlobal(globalPos);
if (currentTabletPointer.currentDevice == QTabletEvent::Airbrush) {
tangentialPressure = localPacketBuf[i].pkTangentPressure
/ qreal(currentTabletPointer.maxTanPressure
- currentTabletPointer.minTanPressure);
} else {
tangentialPressure = 0.0;
}
if (!qt_tablet_tilt_support) {
tiltX = tiltY = 0;
rotation = 0.0;
} else {
ort = localPacketBuf[i].pkOrientation;
// convert from azimuth and altitude to x tilt and y tilt
// what follows is the optimized version. Here are the equations
// I used to get to this point (in case things change :)
// X = sin(azimuth) * cos(altitude)
// Y = cos(azimuth) * cos(altitude)
// Z = sin(altitude)
// X Tilt = arctan(X / Z)
// Y Tilt = arctan(Y / Z)
double radAzim = (ort.orAzimuth / 10) * (Q_PI / 180);
//double radAlt = abs(ort.orAltitude / 10) * (Q_PI / 180);
double tanAlt = tan((abs(ort.orAltitude / 10)) * (Q_PI / 180));
double degX = atan(sin(radAzim) / tanAlt);
double degY = atan(cos(radAzim) / tanAlt);
tiltX = int(degX * (180 / Q_PI));
tiltY = int(-degY * (180 / Q_PI));
// Rotation is measured in degrees. Axis inverted to fit
// the coordinate system of the Linux driver.
rotation = (360 - 1) - ort.orTwist / 10;
}
if (KisTabletDebugger::instance()->debugRawTabletValues()) {
ort = localPacketBuf[i].pkOrientation;
- qDebug() << "WinTab (RS):"
+ dbgKrita << "WinTab (RS):"
<< "NP:" << localPacketBuf[i].pkNormalPressure
<< "TP:" << localPacketBuf[i].pkTangentPressure
<< "Az:" << ort.orAzimuth
<< "Alt:" << ort.orAltitude
<< "Twist:" << ort.orTwist;
}
/**
* Workaround to deal with "inline" tool switches.
* These are caused by the eraser trigger button on the Surface Pro 3.
* We shoot out a tabletUpdateCursor request and a switchInputDevice request.
*/
if (inlineSwitching && !dialogOpen && (localPacketBuf[i].pkCursor != currentCursor)) {
tabletUpdateCursor(localPacketBuf[i].pkCursor);
KoInputDevice id(QTabletEvent::TabletDevice(currentTabletPointer.currentDevice),
QTabletEvent::PointerType(currentTabletPointer.currentPointerType),
currentTabletPointer.llId);
KoToolManager::instance()->switchInputDeviceRequested(id);
}
KisTabletEvent e(t, localPos, globalPos, hiResGlobal, currentTabletPointer.currentDevice,
currentTabletPointer.currentPointerType, prsNew, tiltX, tiltY,
tangentialPressure, rotation, z, modifiers, currentTabletPointer.llId,
button, buttons);
if (button == Qt::NoButton &&
(t == KisTabletEvent::TabletPressEx ||
t == KisTabletEvent::TabletReleaseEx)) {
/**
* Eat events which do not correspond to any mouse
* button. This can happen when the user assinged a stylus
* key to e.g. some keyboard key
*/
e.accept();
} else {
e.ignore();
sendEvent = qApp->sendEvent(w, &e);
}
if (e.isAccepted()) {
globalEventEater->pleaseEatNextEvent(e.getMouseEventType());
} else {
QTabletEvent t = e.toQTabletEvent();
qApp->sendEvent(w, &t);
}
}
return sendEvent;
}
void KisTabletSupportWin::init()
{
globalEventEater = new EventEater(qApp);
qApp->installEventFilter(globalEventEater);
initWinTabFunctions();
}
void KisTabletSupportWin::setButtonsConverter(ButtonsConverter *buttonsConverter)
{
globalButtonsConverter = buttonsConverter;
}
bool KisTabletSupportWin::nativeEventFilter(const QByteArray &/*eventType*/, void *message, long *result)
{
MSG *msg = static_cast<MSG*>(message);
Q_UNUSED(result);
static bool mouseEnteredFlag = false;
switch(msg->message){
case WT_CTXOPEN:
qt_tablet_context = reinterpret_cast<HCTX>(msg->wParam);
break;
case WT_CTXCLOSE:
qt_tablet_context = 0;
break;
case WM_ACTIVATE: {
/**
* Workaround for a focus bug by Qt
*
* Looks like modal windows do not grab focus on Windows. The
* parent widget will still be regarded as a focusWidget()
* although it gets no events. So notify the pure parent that
* he is not in focus anymore.
*/
QWidget *modalWidget = QApplication::activeModalWidget();
if (modalWidget) {
QWidget *focusWidget = QApplication::focusWidget();
if (focusWidget) {
bool active = msg->wParam == WA_ACTIVE || msg->wParam == WA_CLICKACTIVE;
QFocusEvent focusEvent(active ? QEvent::FocusIn : QEvent::FocusOut);
QApplication::sendEvent(focusWidget, &focusEvent);
}
}
break;
}
case WM_MOUSELEAVE:
mouseEnteredFlag = false;
break;
case WM_MOUSEMOVE:
if (qt_tablet_context && !mouseEnteredFlag) {
ptrWTOverlap(qt_tablet_context, true);
mouseEnteredFlag = true;
}
break;
case WT_PROXIMITY: {
const bool enteredProximity = (LOWORD(msg->lParam) != 0);
if (ptrWTPacketsPeek && ptrWTInfo && enteredProximity) {
PACKET proximityBuffer; // we are only interested in the first packet in this case.
const int totalPacks = ptrWTPacketsPeek(qt_tablet_context, 1, &proximityBuffer);
if (totalPacks == 1) {
tabletUpdateCursor(proximityBuffer.pkCursor);
}
}
break;
}
case WT_PACKET: {
Q_ASSERT(qt_tablet_context);
/**
* We eat WT_PACKET events.. We are bad!
*
* But we can do nothing, because the packets are removed from the
* WinTab queue by WTPacketsGet
*/
int nPackets;
if ((nPackets = ptrWTPacketsGet(qt_tablet_context, QT_TABLET_NPACKETQSIZE, &globalPacketBuf))) {
return translateTabletEvent(*msg, globalPacketBuf, nPackets);
}
break;
}
}
return false;
}
diff --git a/krita/ui/input/wintab/kis_tablet_support_x11.cpp b/krita/ui/input/wintab/kis_tablet_support_x11.cpp
index df1a1f89dcf..1d5253cb11c 100644
--- a/krita/ui/input/wintab/kis_tablet_support_x11.cpp
+++ b/krita/ui/input/wintab/kis_tablet_support_x11.cpp
@@ -1,859 +1,859 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tablet_support_x11.h"
#include <QDesktopWidget>
#include <QApplication>
#include <QWidget>
#include <QX11Info>
#include "kis_debug.h"
#include "kis_config.h"
#include <input/kis_tablet_event.h>
#include "kis_tablet_support.h"
#include "wacom-properties.h"
#include <input/kis_tablet_debugger.h>
#include <X11/extensions/XInput.h>
/**
* This is an analog of a Qt's variable qt_tabletChokeMouse. It is
* intended to block Mouse events after any accepted Tablet event. In
* Qt it is available on X11 only, so we won't extend this behavior on
* Windows.
*/
bool kis_tabletChokeMouse = false;
/**
* This variable is true when at least one of our tablet is a Evdev
* one. In such a case we need to request the extension events from
* the server manually
*/
bool kis_haveEvdevTablets = false;
// link Xinput statically
#define XINPUT_LOAD(symbol) symbol
typedef int (*PtrXCloseDevice)(Display *, XDevice *);
typedef XDeviceInfo* (*PtrXListInputDevices)(Display *, int *);
typedef XDevice* (*PtrXOpenDevice)(Display *, XID);
typedef void (*PtrXFreeDeviceList)(XDeviceInfo *);
typedef int (*PtrXSelectExtensionEvent)(Display *, Window, XEventClass *, int);
struct KisX11Data
{
Display *display;
bool use_xinput;
int xinput_major;
int xinput_eventbase;
int xinput_errorbase;
PtrXCloseDevice ptrXCloseDevice;
PtrXListInputDevices ptrXListInputDevices;
PtrXOpenDevice ptrXOpenDevice;
PtrXFreeDeviceList ptrXFreeDeviceList;
PtrXSelectExtensionEvent ptrXSelectExtensionEvent;
/* Warning: if you modify this list, modify the names of atoms in kis_x11_atomnames as well! */
enum X11Atom {
XWacomStylus,
XWacomCursor,
XWacomEraser,
XTabletStylus,
XTabletEraser,
XInputTablet,
XInputKeyboard,
AxisLabels,
ATOM,
AbsX,
AbsY,
AbsPressure,
AbsTiltX,
AbsTiltY,
WacomTouch,
AiptekStylus,
NPredefinedAtoms,
NAtoms = NPredefinedAtoms
};
Atom atoms[NAtoms];
};
/* Warning: if you modify this string, modify the list of atoms in KisX11Data as well! */
static const char kis_x11_atomnames[] = {
// Wacom old. (before version 0.10)
"Wacom Stylus\0"
"Wacom Cursor\0"
"Wacom Eraser\0"
// Tablet
"STYLUS\0"
"ERASER\0"
// XInput tablet device
"TABLET\0"
// Really "nice" Aiptek devices reporting they are a keyboard
"KEYBOARD\0"
// Evdev property that report the assignment of axes
"Axis Labels\0"
"ATOM\0"
// Types of axes reported by evdev
"Abs X\0"
"Abs Y\0"
"Abs Pressure\0"
"Abs Tilt X\0"
"Abs Tilt Y\0"
// Touch capabilities reported by Wacom Intuos tablets
"TOUCH\0"
// Aiptek drivers (e.g. Hyperpen 12000U) reports non-standard type string
"Stylus\0"
};
KisX11Data *kis_x11Data = 0;
#define KIS_ATOM(x) kis_x11Data->atoms[KisX11Data::x]
#define KIS_X11 kis_x11Data
static void kis_x11_create_intern_atoms()
{
const char *names[KisX11Data::NAtoms];
const char *ptr = kis_x11_atomnames;
int i = 0;
while (*ptr) {
names[i++] = ptr;
while (*ptr)
++ptr;
++ptr;
}
Q_ASSERT(i == KisX11Data::NPredefinedAtoms);
Q_ASSERT(i == KisX11Data::NAtoms);
#if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 6)
XInternAtoms(KIS_X11->display, (char **)names, i, False, KIS_X11->atoms);
#else
for (i = 0; i < KisX11Data::NAtoms; ++i)
KIS_X11->atoms[i] = XInternAtom(KIS_X11->display, (char *)names[i], False);
#endif
}
void QTabletDeviceData::SavedAxesData::tryFetchAxesMapping(XDevice *dev)
{
Atom propertyType;
int propertyFormat;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *prop;
int result = XGetDeviceProperty(KIS_X11->display, dev,
KIS_ATOM(AxisLabels),
0, 16, false,
AnyPropertyType,
&propertyType,
&propertyFormat,
&nitems,
&bytes_after,
&prop);
if (result == Success && propertyType > 0) {
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "# Building tablet axes remapping table:";
+ dbgKrita << "# Building tablet axes remapping table:";
}
QVector<AxesIndexes> axesMap(nitems, Unused);
for (unsigned int axisIndex = 0; axisIndex < nitems; axisIndex++) {
Atom currentAxisName = ((Atom*)prop)[axisIndex];
AxesIndexes mappedIndex =
Unused;
if (currentAxisName == KIS_ATOM(AbsX)) {
mappedIndex = XCoord;
} else if (currentAxisName == KIS_ATOM(AbsY)) {
mappedIndex = YCoord;
} else if (currentAxisName == KIS_ATOM(AbsPressure)) {
mappedIndex = Pressure;
} else if (currentAxisName == KIS_ATOM(AbsTiltX)) {
mappedIndex = XTilt;
} else if (currentAxisName == KIS_ATOM(AbsTiltY)) {
mappedIndex = YTilt;
}
axesMap[axisIndex] = mappedIndex;
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << XGetAtomName(KIS_X11->display, currentAxisName)
+ dbgKrita << XGetAtomName(KIS_X11->display, currentAxisName)
<< axisIndex << "->" << mappedIndex;
}
}
this->setAxesMap(axesMap);
}
}
void kis_x11_init_tablet()
{
KisConfig cfg;
bool disableTouchOnCanvas = cfg.disableTouchOnCanvas();
// TODO: free this structure on exit
KIS_X11 = new KisX11Data;
KIS_X11->display = QX11Info::display();
kis_x11_create_intern_atoms();
// XInputExtension
KIS_X11->use_xinput = false;
KIS_X11->xinput_major = 0;
KIS_X11->xinput_eventbase = 0;
KIS_X11->xinput_errorbase = 0;
// See if Xinput is supported on the connected display
KIS_X11->ptrXCloseDevice = 0;
KIS_X11->ptrXListInputDevices = 0;
KIS_X11->ptrXOpenDevice = 0;
KIS_X11->ptrXFreeDeviceList = 0;
KIS_X11->ptrXSelectExtensionEvent = 0;
KIS_X11->use_xinput = XQueryExtension(KIS_X11->display, "XInputExtension", &KIS_X11->xinput_major,
&KIS_X11->xinput_eventbase, &KIS_X11->xinput_errorbase);
if (KIS_X11->use_xinput) {
KIS_X11->ptrXCloseDevice = XINPUT_LOAD(XCloseDevice);
KIS_X11->ptrXListInputDevices = XINPUT_LOAD(XListInputDevices);
KIS_X11->ptrXOpenDevice = XINPUT_LOAD(XOpenDevice);
KIS_X11->ptrXFreeDeviceList = XINPUT_LOAD(XFreeDeviceList);
KIS_X11->ptrXSelectExtensionEvent = XINPUT_LOAD(XSelectExtensionEvent);
}
if (KIS_X11->use_xinput) {
int ndev,
i,
j;
bool gotStylus,
gotEraser;
XDeviceInfo *devices = 0, *devs;
XInputClassInfo *ip;
XAnyClassPtr any;
XValuatorInfoPtr v;
XAxisInfoPtr a;
XDevice *dev = 0;
bool needCheckIfItIsReallyATablet;
bool touchWacomTabletWorkaround;
if (KIS_X11->ptrXListInputDevices) {
devices = KIS_X11->ptrXListInputDevices(KIS_X11->display, &ndev);
if (!devices)
qWarning("QApplication: Failed to get list of tablet devices");
}
if (!devices)
ndev = -1;
QTabletEvent::TabletDevice deviceType;
for (devs = devices, i = 0; i < ndev && devs; i++, devs++) {
dev = 0;
deviceType = QTabletEvent::NoDevice;
gotStylus = false;
gotEraser = false;
needCheckIfItIsReallyATablet = false;
touchWacomTabletWorkaround = false;
#if defined(Q_OS_IRIX)
#else
if (devs->type == KIS_ATOM(XWacomStylus) || devs->type == KIS_ATOM(XTabletStylus) ||devs->type == KIS_ATOM(XInputTablet)) {
if (devs->type == KIS_ATOM(XInputTablet)) {
kis_haveEvdevTablets = true;
}
deviceType = QTabletEvent::Stylus;
gotStylus = true;
} else if (devs->type == KIS_ATOM(XWacomEraser) || devs->type == KIS_ATOM(XTabletEraser)) {
deviceType = QTabletEvent::XFreeEraser;
gotEraser = true;
} else if ((devs->type == KIS_ATOM(XInputKeyboard) ||
devs->type == KIS_ATOM(AiptekStylus))
&& QString(devs->name) == "Aiptek") {
/**
* Some really "nice" tablets (more precisely,
* Genius G-Pen 510 (aiptek driver)) report that
* they are a "keyboard". Well, we cannot convince
* them that they are not, so just check if this
* "keyboard" has motion and proximity events. If
* it looks like a duck... :)
*/
kis_haveEvdevTablets = true;
deviceType = QTabletEvent::Stylus;
gotStylus = true;
needCheckIfItIsReallyATablet = true;
} else if (disableTouchOnCanvas &&
devs->type == KIS_ATOM(WacomTouch) &&
QString(devs->name).contains("Wacom")) {
kis_haveEvdevTablets = true;
deviceType = QTabletEvent::Stylus;
gotStylus = true;
touchWacomTabletWorkaround = true;
}
#endif
if (deviceType == QTabletEvent::NoDevice)
continue;
if (gotStylus || gotEraser) {
if (KIS_X11->ptrXOpenDevice)
dev = KIS_X11->ptrXOpenDevice(KIS_X11->display, devs->id);
if (!dev)
continue;
QTabletDeviceData device_data;
device_data.deviceType = deviceType;
device_data.eventCount = 0;
device_data.device = dev;
device_data.xinput_motion = -1;
device_data.xinput_key_press = -1;
device_data.xinput_key_release = -1;
device_data.xinput_button_press = -1;
device_data.xinput_button_release = -1;
device_data.xinput_proximity_in = -1;
device_data.xinput_proximity_out = -1;
device_data.isTouchWacomTablet = touchWacomTabletWorkaround;
//device_data.widgetToGetPress = 0;
if (dev->num_classes > 0) {
for (ip = dev->classes, j = 0; j < dev->num_classes;
ip++, j++) {
switch (ip->input_class) {
case KeyClass:
DeviceKeyPress(dev, device_data.xinput_key_press,
device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
DeviceKeyRelease(dev, device_data.xinput_key_release,
device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
break;
case ButtonClass:
DeviceButtonPress(dev, device_data.xinput_button_press,
device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
DeviceButtonRelease(dev, device_data.xinput_button_release,
device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
break;
case ValuatorClass:
// I'm only going to be interested in motion when the
// stylus is already down anyway!
DeviceMotionNotify(dev, device_data.xinput_motion,
device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
ProximityIn(dev, device_data.xinput_proximity_in, device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
ProximityOut(dev, device_data.xinput_proximity_out, device_data.eventList[device_data.eventCount]);
if (device_data.eventList[device_data.eventCount])
++device_data.eventCount;
default:
break;
}
}
}
if (needCheckIfItIsReallyATablet &&
(device_data.xinput_motion == -1 ||
device_data.xinput_proximity_in == -1 ||
device_data.xinput_proximity_out == -1)) {
continue;
}
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "###################################";
- qDebug() << "# Adding a tablet device:" << devs->name;
- qDebug() << "Device Type:" << KisTabletDebugger::tabletDeviceToString(deviceType);
+ dbgKrita << "###################################";
+ dbgKrita << "# Adding a tablet device:" << devs->name;
+ dbgKrita << "Device Type:" << KisTabletDebugger::tabletDeviceToString(deviceType);
}
device_data.savedAxesData.tryFetchAxesMapping(dev);
// get the min/max value for pressure!
any = (XAnyClassPtr) (devs->inputclassinfo);
for (j = 0; j < devs->num_classes; j++) {
if (any->c_class == ValuatorClass) {
v = (XValuatorInfoPtr) any;
a = (XAxisInfoPtr) ((char *) v +
sizeof (XValuatorInfo));
#if defined (Q_OS_IRIX)
#else
device_data.minX = a[0].min_value;
device_data.maxX = a[0].max_value;
device_data.minY = a[1].min_value;
device_data.maxY = a[1].max_value;
device_data.minPressure = a[2].min_value;
device_data.maxPressure = a[2].max_value;
device_data.minTanPressure = 0;
device_data.maxTanPressure = 0;
device_data.minZ = 0;
device_data.maxZ = 0;
device_data.minRotation = a[5].min_value;
device_data.maxRotation = a[5].max_value;
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
- qDebug() << "# Axes limits data";
- qDebug() << "X: " << device_data.minX << device_data.maxX;
- qDebug() << "Y: " << device_data.minY << device_data.maxY;
- qDebug() << "Z: " << device_data.minZ << device_data.maxZ;
- qDebug() << "Pressure:" << device_data.minPressure << device_data.maxPressure;
- qDebug() << "Rotation:" << device_data.minRotation << device_data.maxRotation;
- qDebug() << "T. Pres: " << device_data.minTanPressure << device_data.maxTanPressure;
+ dbgKrita << "# Axes limits data";
+ dbgKrita << "X: " << device_data.minX << device_data.maxX;
+ dbgKrita << "Y: " << device_data.minY << device_data.maxY;
+ dbgKrita << "Z: " << device_data.minZ << device_data.maxZ;
+ dbgKrita << "Pressure:" << device_data.minPressure << device_data.maxPressure;
+ dbgKrita << "Rotation:" << device_data.minRotation << device_data.maxRotation;
+ dbgKrita << "T. Pres: " << device_data.minTanPressure << device_data.maxTanPressure;
}
#endif
// got the max pressure no need to go further...
break;
}
any = (XAnyClassPtr) ((char *) any + any->length);
} // end of for loop
qt_tablet_devices()->append(device_data);
} // if (gotStylus || gotEraser)
}
if (KIS_X11->ptrXFreeDeviceList)
KIS_X11->ptrXFreeDeviceList(devices);
}
}
void fetchWacomToolId(qint64 &serialId, qint64 &deviceId, QTabletDeviceData *tablet)
{
XDevice *dev = static_cast<XDevice*>(tablet->device);
Atom prop = None, type;
int format;
unsigned char* data;
unsigned long nitems, bytes_after;
prop = XInternAtom(KIS_X11->display, WACOM_PROP_SERIALIDS, True);
if (!prop) {
// property doesn't exist
return;
}
XGetDeviceProperty(KIS_X11->display, dev, prop, 0, 1000, False, AnyPropertyType,
&type, &format, &nitems, &bytes_after, &data);
if (nitems < 5 || format != 32) {
// property offset doesn't exist
return;
}
long *l = (long*)data;
serialId = l[3]; // serial id of the stylus in proximity
deviceId = l[4]; // device if of the stylus in proximity
}
static Qt::MouseButtons translateMouseButtons(int s)
{
Qt::MouseButtons ret = 0;
if (s & Button1Mask)
ret |= Qt::LeftButton;
if (s & Button2Mask)
ret |= Qt::MidButton;
if (s & Button3Mask)
ret |= Qt::RightButton;
return ret;
}
static Qt::MouseButton translateMouseButton(int b)
{
return b == Button1 ? Qt::LeftButton :
b == Button2 ? Qt::MidButton :
b == Button3 ? Qt::RightButton :
Qt::LeftButton /* fallback */;
}
QTabletEvent::TabletDevice parseWacomDeviceId(quint32 deviceId)
{
enum {
CSR_TYPE_SPECIFIC_MASK = 0x0F06,
CSR_TYPE_SPECIFIC_STYLUS_BITS = 0x0802,
CSR_TYPE_SPECIFIC_AIRBRUSH_BITS = 0x0902,
CSR_TYPE_SPECIFIC_4DMOUSE_BITS = 0x0004,
CSR_TYPE_SPECIFIC_LENSCURSOR_BITS = 0x0006,
CSR_TYPE_SPECIFIC_ROTATIONSTYLUS_BITS = 0x0804
};
QTabletEvent::TabletDevice device;
if ((((deviceId & 0x0006) == 0x0002) &&
((deviceId & CSR_TYPE_SPECIFIC_MASK) != CSR_TYPE_SPECIFIC_AIRBRUSH_BITS)) || // Bamboo
deviceId == 0x4020) { // Surface Pro 2 tablet device
device = QTabletEvent::Stylus;
} else {
switch (deviceId & CSR_TYPE_SPECIFIC_MASK) {
case CSR_TYPE_SPECIFIC_STYLUS_BITS:
device = QTabletEvent::Stylus;
break;
case CSR_TYPE_SPECIFIC_AIRBRUSH_BITS:
device = QTabletEvent::Airbrush;
break;
case CSR_TYPE_SPECIFIC_4DMOUSE_BITS:
device = QTabletEvent::FourDMouse;
break;
case CSR_TYPE_SPECIFIC_LENSCURSOR_BITS:
device = QTabletEvent::Puck;
break;
case CSR_TYPE_SPECIFIC_ROTATIONSTYLUS_BITS:
device = QTabletEvent::RotationStylus;
break;
default:
if (deviceId != 0) {
- qWarning() << "Unrecognized stylus device found! Falling back to usual \'Stylus\' definition";
- qWarning() << ppVar(deviceId);
- qWarning() << ppVar((deviceId & CSR_TYPE_SPECIFIC_MASK));
+ warnKrita << "Unrecognized stylus device found! Falling back to usual \'Stylus\' definition";
+ warnKrita << ppVar(deviceId);
+ warnKrita << ppVar((deviceId & CSR_TYPE_SPECIFIC_MASK));
}
device = QTabletEvent::Stylus;
}
}
return device;
}
bool translateXinputEvent(const XEvent *ev, QTabletDeviceData *tablet, QWidget *defaultWidget)
{
Q_ASSERT(defaultWidget);
#if defined (Q_OS_IRIX)
#endif
Q_ASSERT(tablet != 0);
QWidget *w = defaultWidget;
QPoint global,
curr;
QPointF hiRes;
qreal pressure = 0;
int xTilt = 0,
yTilt = 0,
z = 0;
qreal tangentialPressure = 0;
qreal rotation = 0;
int deviceType = QTabletEvent::NoDevice;
int pointerType = QTabletEvent::UnknownPointer;
const XDeviceMotionEvent *motion = 0;
XDeviceButtonEvent *button = 0;
KisTabletEvent::ExtraEventType t = KisTabletEvent::TabletMoveEx;
Qt::KeyboardModifiers modifiers = 0;
modifiers = QApplication::queryKeyboardModifiers();
#if !defined (Q_OS_IRIX)
XID device_id = 0;
#endif
if (ev->type == tablet->xinput_motion) {
motion = reinterpret_cast<const XDeviceMotionEvent*>(ev);
t = KisTabletEvent::TabletMoveEx;
global = QPoint(motion->x_root, motion->y_root);
curr = QPoint(motion->x, motion->y);
#if !defined (Q_OS_IRIX)
device_id = motion->deviceid;
#endif
} else if (ev->type == tablet->xinput_button_press || ev->type == tablet->xinput_button_release) {
if (ev->type == tablet->xinput_button_press) {
t = KisTabletEvent::TabletPressEx;
} else {
t = KisTabletEvent::TabletReleaseEx;
}
button = (XDeviceButtonEvent*)ev;
global = QPoint(button->x_root, button->y_root);
curr = QPoint(button->x, button->y);
#if !defined (Q_OS_IRIX)
device_id = button->deviceid;
#endif
} else {
qFatal("Unknown event type! Probably, 'proximity', "
"but we don't handle it here, so this is a bug");
}
qint64 wacomSerialId = 0;
qint64 wacomDeviceId = 0;
#if defined (Q_OS_IRIX)
#else
// We've been passed in data for a tablet device that handles this type
// of event, but it isn't necessarily the tablet device that originated
// the event. Use the device id to find the originating device if we
// have it.
QTabletDeviceDataList *tablet_list = qt_tablet_devices();
for (int i = 0; i < tablet_list->size(); ++i) {
QTabletDeviceData &tab = tablet_list->operator[](i);
if (device_id == static_cast<XDevice *>(tab.device)->device_id) {
// Replace the tablet passed in with this one.
tablet = &tab;
deviceType = tab.deviceType;
if (tab.deviceType == QTabletEvent::XFreeEraser) {
deviceType = QTabletEvent::Stylus;
pointerType = QTabletEvent::Eraser;
} else if (tab.deviceType == QTabletEvent::Stylus) {
pointerType = QTabletEvent::Pen;
}
break;
}
}
/**
* Touch events from Wacom tablets should not be sent as real
* tablet events
*/
if (tablet->isTouchWacomTablet) return false;
fetchWacomToolId(wacomSerialId, wacomDeviceId, tablet);
if (wacomDeviceId && deviceType == QTabletEvent::Stylus) {
deviceType = parseWacomDeviceId(wacomDeviceId);
}
QRect screenArea = qApp->desktop()->rect();
/**
* Some 'nice' tablet drivers (evdev) do not return the value
* of all the axes each time. Instead they tell about the
* recenty changed axes only, so we have to keep the state of
* all the axes internally and update the relevant part only.
*/
bool hasSaneData = false;
if (motion) {
hasSaneData =
tablet->savedAxesData.updateAxesData(motion->first_axis,
motion->axes_count,
motion->axis_data);
} else if (button) {
hasSaneData =
tablet->savedAxesData.updateAxesData(button->first_axis,
button->axes_count,
button->axis_data);
}
if (!hasSaneData) return false;
hiRes = tablet->savedAxesData.position(tablet, screenArea);
pressure = tablet->savedAxesData.pressure();
xTilt = tablet->savedAxesData.xTilt();
yTilt = tablet->savedAxesData.yTilt();
if (deviceType == QTabletEvent::Airbrush) {
tangentialPressure =
std::fmod(qreal(tablet->savedAxesData.rotation() - tablet->minRotation) /
(tablet->maxRotation - tablet->minRotation) , 2.0);
} else {
rotation =
std::fmod(qreal(tablet->savedAxesData.rotation() - tablet->minRotation) /
(tablet->maxRotation - tablet->minRotation) + 0.5, 1.0) * 360.0;
}
#endif
if (tablet->widgetToGetPress) {
w = tablet->widgetToGetPress;
} else {
QWidget *child = w->childAt(curr);
if (child)
w = child;
}
curr = w->mapFromGlobal(global);
if (t == KisTabletEvent::TabletPressEx) {
tablet->widgetToGetPress = w;
} else if (t == KisTabletEvent::TabletReleaseEx && tablet->widgetToGetPress) {
w = tablet->widgetToGetPress;
curr = w->mapFromGlobal(global);
tablet->widgetToGetPress = 0;
}
Qt::MouseButton qtbutton = Qt::NoButton;
Qt::MouseButtons qtbuttons;
if (motion) {
qtbuttons = translateMouseButtons(motion->state);
} else if (button) {
qtbuttons = translateMouseButtons(button->state);
qtbutton = translateMouseButton(button->button);
}
KisTabletEvent e(t, curr, global, hiRes,
deviceType, pointerType,
qreal(pressure / qreal(tablet->maxPressure - tablet->minPressure)),
xTilt, yTilt, tangentialPressure, rotation, z, modifiers, wacomSerialId,
qtbutton, qtbuttons);
e.ignore();
QApplication::sendEvent(w, &e);
return e.isAccepted();
}
void KisTabletSupportX11::init()
{
kis_x11_init_tablet();
}
void evdevEventsActivationWorkaround(WId window)
{
/**
* Evdev devices send us events *only* in case we requested
* them for every window which desires to get them, so just
* do it as it wants
*/
static QSet<WId> registeredWindows;
if (registeredWindows.contains(window)) return;
registeredWindows.insert(window);
QTabletDeviceDataList *tablets = qt_tablet_devices();
for (int i = 0; i < tablets->size(); ++i) {
QTabletDeviceData &tab = tablets->operator [](i);
XSelectExtensionEvent(KIS_X11->display,
window,
tab.eventList,
tab.eventCount);
}
}
bool KisTabletSupportX11::nativeEventFilter(const QByteArray &/*eventType*/, void *ev, long * /*unused_on_X11*/)
{
XEvent *event = static_cast<XEvent*>(ev);
// Eat the choked mouse event...
if (kis_tabletChokeMouse &&
(event->type == ButtonRelease ||
event->type == ButtonPress ||
event->type == MotionNotify)) {
kis_tabletChokeMouse = false;
// Mhom-mhom...
return true;
}
if (kis_haveEvdevTablets && event->type == EnterNotify) {
evdevEventsActivationWorkaround((WId)event->xany.window);
}
QTabletDeviceDataList *tablets = qt_tablet_devices();
for (int i = 0; i < tablets->size(); ++i) {
QTabletDeviceData &tab = tablets->operator [](i);
if (event->type == tab.xinput_motion
|| event->type == tab.xinput_button_release
|| event->type == tab.xinput_button_press) {
QWidget *widget = QApplication::activePopupWidget();
if (!widget) {
widget = QApplication::activeModalWidget();
}
if (!widget) {
widget = QWidget::find((WId)event->xany.window);
}
bool retval = widget ? translateXinputEvent(event, &tab, widget) : false;
if (retval) {
/**
* If the tablet event is accepted, no mouse event
* should arrive. Otherwise, the popup widgets (at
* least) will not work correctly
*/
kis_tabletChokeMouse = true;
}
return retval;
} else if (event->type == tab.xinput_proximity_in ||
event->type == tab.xinput_proximity_out) {
const XProximityNotifyEvent *proximity =
reinterpret_cast<const XProximityNotifyEvent*>(event);
XID device_id = proximity->deviceid;
QTabletDeviceDataList *tablet_list = qt_tablet_devices();
for (int i = 0; i < tablet_list->size(); ++i) {
QTabletDeviceData &tab = tablet_list->operator[](i);
if (device_id == static_cast<XDevice *>(tab.device)->device_id &&
tab.isTouchWacomTablet) {
QWidget *widget = QApplication::activePopupWidget();
if (!widget) {
widget = QApplication::activeModalWidget();
}
if (!widget) {
widget = QWidget::find((WId)event->xany.window);
}
if (widget) {
QPoint curr(proximity->x, proximity->y);
QWidget *child = widget->childAt(curr);
if (child) {
widget = child;
}
QEvent::Type type = (QEvent::Type)
(event->type == tab.xinput_proximity_in ?
KisTabletEvent::TouchProximityInEx :
KisTabletEvent::TouchProximityOutEx);
QEvent e(type);
e.ignore();
QApplication::sendEvent(widget, &e);
}
return true;
}
}
}
}
return false;
}
diff --git a/krita/ui/kis_action.h b/krita/ui/kis_action.h
index dfb105b4216..ec4c9d0bb7e 100644
--- a/krita/ui/kis_action.h
+++ b/krita/ui/kis_action.h
@@ -1,106 +1,106 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ACTION_H
#define KIS_ACTION_H
#include <kaction.h>
#include <kritaui_export.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kicon.h>
class KisActionManager;
class KRITAUI_EXPORT KisAction : public KAction
{
Q_OBJECT
public:
enum ActivationFlag {
NONE = 0x0000, ///< Always activate
ACTIVE_IMAGE = 0x0001, ///< Activate if there is at least one image
MULTIPLE_IMAGES = 0x0002, ///< Activate if there is more than one image open
CURRENT_IMAGE_MODIFIED = 0x0004, ///< Activate if the current image is modified
ACTIVE_NODE = 0x0008, ///< Activate if there's an active node (layer or mask)
ACTIVE_DEVICE = 0x0010, ///< Activate if the active node has a paint device, i.e. there are pixels to be modified
ACTIVE_LAYER = 0x0020, ///< Activate if the current node is a layer (vector or pixel)
ACTIVE_TRANSPARENCY_MASK = 0x0040, ///< Activate if the current node is a transparency mask
ACTIVE_SHAPE_LAYER = 0x0080, ///< Activate if the current node is a vector layer
PIXELS_SELECTED = 0x0100, ///< Activate if there is an active pixel selection
SHAPES_SELECTED = 0x0200, ///< Activate if there is an active vector selection
PIXEL_SELECTION_WITH_PIXELS = 0x0400, ///< ???
PIXELS_IN_CLIPBOARD = 0x0800, ///< Activate if the clipboard contains pixels
SHAPES_IN_CLIPBOARD = 0x1000, ///< Activate if the clipboard contains vector data
NEVER_ACTIVATE = 0x2000, ///<
};
Q_DECLARE_FLAGS(ActivationFlags, ActivationFlag)
enum ActivationCondition {
NO_CONDITION = 0,
ACTIVE_NODE_EDITABLE = 0x1,
ACTIVE_NODE_EDITABLE_PAINT_DEVICE = 0x2,
SELECTION_EDITABLE = 0x4
};
Q_DECLARE_FLAGS(ActivationConditions, ActivationCondition)
explicit KisAction(QObject* parent = 0);
KisAction(const QString& text, QObject* parent = 0);
KisAction(const KIcon& icon, const QString& text, QObject* parent = 0);
virtual ~KisAction();
void setActivationFlags(ActivationFlags flags);
ActivationFlags activationFlags();
void setActivationConditions(ActivationConditions conditions);
ActivationConditions activationConditions();
void setExcludedNodeTypes(const QStringList &nodeTypes);
const QStringList& excludedNodeTypes() const;
virtual void setActionEnabled(bool enabled);
/**
* Set operation id. This will used to run an operation in the KisActionManager
*/
void setOperationID(const QString& id);
Q_SIGNALS:
void sigEnableSlaves(bool value);
private Q_SLOTS:
void slotTriggered();
void slotChanged();
private:
friend class KisActionManager;
/**
* Set the action manager. Only used by KisActionManager
*/
void setActionManager(KisActionManager* actionManager);
class Private;
Private* const d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KisAction::ActivationFlags)
Q_DECLARE_OPERATORS_FOR_FLAGS(KisAction::ActivationConditions)
#endif // KIS_ACTION_H
diff --git a/krita/ui/kis_asl_layer_style_serializer.cpp b/krita/ui/kis_asl_layer_style_serializer.cpp
index a7ce24ffeb9..d91c6ad6f8f 100644
--- a/krita/ui/kis_asl_layer_style_serializer.cpp
+++ b/krita/ui/kis_asl_layer_style_serializer.cpp
@@ -1,1238 +1,1238 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_layer_style_serializer.h"
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <QDomDocument>
#include <KoResourceServerProvider.h>
#include <KoAbstractGradient.h>
#include <KoSegmentGradient.h>
#include <KoStopGradient.h>
#include <KoPattern.h>
#include "kis_dom_utils.h"
#include "psd.h"
#include "kis_global.h"
#include "kis_psd_layer_style.h"
#include "asl/kis_asl_reader.h"
#include "asl/kis_asl_xml_parser.h"
#include "asl/kis_asl_callback_object_catcher.h"
#include "asl/kis_asl_writer_utils.h"
#include "asl/kis_asl_xml_writer.h"
#include "asl/kis_asl_writer.h"
KisAslLayerStyleSerializer::KisAslLayerStyleSerializer()
{
}
KisAslLayerStyleSerializer::~KisAslLayerStyleSerializer()
{
}
QVector<KisPSDLayerStyleSP> KisAslLayerStyleSerializer::styles() const
{
return m_stylesVector;
}
void KisAslLayerStyleSerializer::setStyles(const QVector<KisPSDLayerStyleSP> &styles)
{
m_stylesVector = styles;
}
QString compositeOpToBlendMode(const QString &compositeOp)
{
QString mode = "Nrml";
if (compositeOp == COMPOSITE_OVER) {
mode = "Nrml";
} else if (compositeOp == COMPOSITE_DISSOLVE) {
mode = "Dslv";
} else if (compositeOp == COMPOSITE_DARKEN) {
mode = "Drkn";
} else if (compositeOp == COMPOSITE_MULT) {
mode = "Mltp";
} else if (compositeOp == COMPOSITE_BURN) {
mode = "CBrn";
} else if (compositeOp == COMPOSITE_LINEAR_BURN) {
mode = "linearBurn";
} else if (compositeOp == COMPOSITE_DARKER_COLOR) {
mode = "darkerColor";
} else if (compositeOp == COMPOSITE_LIGHTEN) {
mode = "Lghn";
} else if (compositeOp == COMPOSITE_SCREEN) {
mode = "Scrn";
} else if (compositeOp == COMPOSITE_DODGE) {
mode = "CDdg";
} else if (compositeOp == COMPOSITE_LINEAR_DODGE) {
mode = "linearDodge";
} else if (compositeOp == COMPOSITE_LIGHTER_COLOR) {
mode = "lighterColor";
} else if (compositeOp == COMPOSITE_OVERLAY) {
mode = "Ovrl";
} else if (compositeOp == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) {
mode = "SftL";
} else if (compositeOp == COMPOSITE_HARD_LIGHT) {
mode = "HrdL";
} else if (compositeOp == COMPOSITE_VIVID_LIGHT) {
mode = "vividLight";
} else if (compositeOp == COMPOSITE_LINEAR_LIGHT) {
mode = "linearLight";
} else if (compositeOp == COMPOSITE_PIN_LIGHT) {
mode = "pinLight";
} else if (compositeOp == COMPOSITE_HARD_MIX) {
mode = "hardMix";
} else if (compositeOp == COMPOSITE_DIFF) {
mode = "Dfrn";
} else if (compositeOp == COMPOSITE_EXCLUSION) {
mode = "Xclu";
} else if (compositeOp == COMPOSITE_SUBTRACT) {
mode = "Sbtr";
} else if (compositeOp == COMPOSITE_DIVIDE) {
mode = "divide";
} else if (compositeOp == COMPOSITE_HUE) {
mode = "H ";
} else if (compositeOp == COMPOSITE_SATURATION) {
mode = "Strt";
} else if (compositeOp == COMPOSITE_COLOR) {
mode = "Clr ";
} else if (compositeOp == COMPOSITE_LUMINIZE) {
mode = "Lmns";
} else {
- qDebug() << "Unknown composite op:" << mode << "Returning \"Nrml\"!";
+ dbgKrita << "Unknown composite op:" << mode << "Returning \"Nrml\"!";
}
return mode;
}
QString techniqueToString(psd_technique_type technique, const QString &typeId)
{
QString result = "SfBL";
switch (technique) {
case psd_technique_softer:
result = "SfBL";
break;
case psd_technique_precise:
result = "PrBL";
break;
case psd_technique_slope_limit:
result = "Slmt";
break;
}
if (typeId == "BETE" && technique == psd_technique_slope_limit) {
- qWarning() << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId);
+ warnKrita << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId);
}
return result;
}
QString bevelStyleToString(psd_bevel_style style)
{
QString result = "OtrB";
switch (style) {
case psd_bevel_outer_bevel:
result = "OtrB";
break;
case psd_bevel_inner_bevel:
result = "InrB";
break;
case psd_bevel_emboss:
result = "Embs";
break;
case psd_bevel_pillow_emboss:
result = "PlEb";
break;
case psd_bevel_stroke_emboss:
result = "strokeEmboss";
break;
}
return result;
}
QString gradientTypeToString(psd_gradient_style style)
{
QString result = "Lnr ";
switch (style) {
case psd_gradient_style_linear:
result = "Lnr ";
break;
case psd_gradient_style_radial:
result = "Rdl ";
break;
case psd_gradient_style_angle:
result = "Angl";
break;
case psd_gradient_style_reflected:
result = "Rflc";
break;
case psd_gradient_style_diamond:
result = "Dmnd";
break;
}
return result;
}
QString strokePositionToString(psd_stroke_position position)
{
QString result = "OutF";
switch (position) {
case psd_stroke_outside:
result = "OutF";
break;
case psd_stroke_inside:
result = "InsF";
break;
case psd_stroke_center:
result = "CtrF";
break;
}
return result;
}
QString strokeFillTypeToString(psd_fill_type position)
{
QString result = "SClr";
switch (position) {
case psd_fill_solid_color:
result = "SClr";
break;
case psd_fill_gradient:
result = "GrFl";
break;
case psd_fill_pattern:
result = "Ptrn";
break;
}
return result;
}
QVector<KoPattern*> KisAslLayerStyleSerializer::fetchAllPatterns(KisPSDLayerStyle *style) const
{
QVector <KoPattern*> allPatterns;
if (style->patternOverlay()->effectEnabled()) {
allPatterns << style->patternOverlay()->pattern();
}
if (style->stroke()->effectEnabled() &&
style->stroke()->fillType() == psd_fill_pattern) {
allPatterns << style->stroke()->pattern();
}
if(style->bevelAndEmboss()->effectEnabled() &&
style->bevelAndEmboss()->textureEnabled()) {
allPatterns << style->bevelAndEmboss()->texturePattern();
}
return allPatterns;
}
QString fetchPatternUuidSafe(KoPattern *pattern, QHash<KoPattern*, QString> patternToUuid)
{
if (patternToUuid.contains(pattern)) {
return patternToUuid[pattern];
} else {
- qWarning() << "WARNING: the pattern is not present in the Uuid map!";
+ warnKrita << "WARNING: the pattern is not present in the Uuid map!";
return "invalid-uuid";
}
}
QDomDocument KisAslLayerStyleSerializer::formXmlDocument() const
{
KIS_ASSERT_RECOVER(!m_stylesVector.isEmpty()) { return QDomDocument(); }
QVector<KoPattern*> allPatterns;
foreach (KisPSDLayerStyleSP style, m_stylesVector) {
allPatterns += fetchAllPatterns(style.data());
}
QHash<KoPattern*, QString> patternToUuidMap;
KisAslXmlWriter w;
if (!allPatterns.isEmpty()) {
w.enterList("Patterns");
foreach (KoPattern *pattern, allPatterns) {
if (pattern) {
if (!patternToUuidMap.contains(pattern)) {
QString uuid = w.writePattern("", pattern);
patternToUuidMap.insert(pattern, uuid);
}
} else {
- qWarning() << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!";
+ warnKrita << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!";
}
}
w.leaveList();
}
foreach (KisPSDLayerStyleSP style, m_stylesVector) {
w.enterDescriptor("", "", "null");
w.writeText("Nm ", style->name());
w.writeText("Idnt", style->psdUuid());
w.leaveDescriptor();
w.enterDescriptor("", "", "Styl");
w.enterDescriptor("documentMode", "", "documentMode");
w.leaveDescriptor();
w.enterDescriptor("Lefx", "", "Lefx");
w.writeUnitFloat("Scl ", "#Prc", 100);
w.writeBoolean("masterFXSwitch", style->isEnabled());
// Drop Shadow
const psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
if (dropShadow->effectEnabled()) {
w.enterDescriptor("DrSh", "", "DrSh");
w.writeBoolean("enab", dropShadow->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(dropShadow->blendMode()));
w.writeColor("Clr ", dropShadow->color());
w.writeUnitFloat("Opct", "#Prc", dropShadow->opacity());
w.writeBoolean("uglg", dropShadow->useGlobalLight());
w.writeUnitFloat("lagl", "#Ang", dropShadow->angle());
w.writeUnitFloat("Dstn", "#Pxl", dropShadow->distance());
w.writeUnitFloat("Ckmt", "#Pxl", dropShadow->spread());
w.writeUnitFloat("blur", "#Pxl", dropShadow->size());
w.writeUnitFloat("Nose", "#Prc", dropShadow->noise());
w.writeBoolean("AntA", dropShadow->antiAliased());
// FIXME: save curves
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeBoolean("layerConceals", dropShadow->knocksOut());
w.leaveDescriptor();
}
// Inner Shadow
const psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
if (innerShadow->effectEnabled()) {
w.enterDescriptor("IrSh", "", "IrSh");
w.writeBoolean("enab", innerShadow->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerShadow->blendMode()));
w.writeColor("Clr ", innerShadow->color());
w.writeUnitFloat("Opct", "#Prc", innerShadow->opacity());
w.writeBoolean("uglg", innerShadow->useGlobalLight());
w.writeUnitFloat("lagl", "#Ang", innerShadow->angle());
w.writeUnitFloat("Dstn", "#Pxl", innerShadow->distance());
w.writeUnitFloat("Ckmt", "#Pxl", innerShadow->spread());
w.writeUnitFloat("blur", "#Pxl", innerShadow->size());
w.writeUnitFloat("Nose", "#Prc", innerShadow->noise());
w.writeBoolean("AntA", innerShadow->antiAliased());
// FIXME: save curves
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.leaveDescriptor();
}
// Outer Glow
const psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
if (outerGlow->effectEnabled()) {
w.enterDescriptor("OrGl", "", "OrGl");
w.writeBoolean("enab", outerGlow->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(outerGlow->blendMode()));
if (outerGlow->fillType() == psd_fill_gradient && outerGlow->gradient()) {
KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(outerGlow->gradient().data());
KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(outerGlow->gradient().data());
if (segmentGradient) {
w.writeSegmentGradient("Grad", segmentGradient);
} else if (stopGradient) {
w.writeStopGradient("Grad", stopGradient);
} else {
- qWarning() << "WARNING: OG: Unknown gradient type!";
+ warnKrita << "WARNING: OG: Unknown gradient type!";
w.writeColor("Clr ", outerGlow->color());
}
} else {
w.writeColor("Clr ", outerGlow->color());
}
w.writeUnitFloat("Opct", "#Prc", outerGlow->opacity());
w.writeEnum("GlwT", "BETE", techniqueToString(outerGlow->technique(), "BETE"));
w.writeUnitFloat("Ckmt", "#Pxl", outerGlow->spread());
w.writeUnitFloat("blur", "#Pxl", outerGlow->size());
w.writeUnitFloat("Nose", "#Prc", outerGlow->noise());
w.writeUnitFloat("ShdN", "#Prc", outerGlow->jitter());
w.writeBoolean("AntA", outerGlow->antiAliased());
// FIXME: save curves
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeUnitFloat("Inpr", "#Prc", outerGlow->range());
w.leaveDescriptor();
}
// Inner Glow
const psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
if (innerGlow->effectEnabled()) {
w.enterDescriptor("IrGl", "", "IrGl");
w.writeBoolean("enab", innerGlow->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerGlow->blendMode()));
if (innerGlow->fillType() == psd_fill_gradient && innerGlow->gradient()) {
KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(innerGlow->gradient().data());
KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(innerGlow->gradient().data());
if (segmentGradient) {
w.writeSegmentGradient("Grad", segmentGradient);
} else if (stopGradient) {
w.writeStopGradient("Grad", stopGradient);
} else {
- qWarning() << "WARNING: IG: Unknown gradient type!";
+ warnKrita << "WARNING: IG: Unknown gradient type!";
w.writeColor("Clr ", innerGlow->color());
}
} else {
w.writeColor("Clr ", innerGlow->color());
}
w.writeUnitFloat("Opct", "#Prc", innerGlow->opacity());
w.writeEnum("GlwT", "BETE", techniqueToString(innerGlow->technique(), "BETE"));
w.writeUnitFloat("Ckmt", "#Pxl", innerGlow->spread());
w.writeUnitFloat("blur", "#Pxl", innerGlow->size());
// NOTE: order is swapped in ASL!
w.writeUnitFloat("ShdN", "#Prc", innerGlow->jitter());
w.writeUnitFloat("Nose", "#Prc", innerGlow->noise());
w.writeBoolean("AntA", innerGlow->antiAliased());
w.writeEnum("glwS", "IGSr", innerGlow->source() == psd_glow_center ? "SrcC" : "SrcE");
// FIXME: save curves
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeUnitFloat("Inpr", "#Prc", innerGlow->range());
w.leaveDescriptor();
}
// Bevel and Emboss
const psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
if (bevelAndEmboss->effectEnabled()) {
w.enterDescriptor("ebbl", "", "ebbl");
w.writeBoolean("enab", bevelAndEmboss->effectEnabled());
w.writeEnum("hglM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->highlightBlendMode()));
w.writeColor("hglC", bevelAndEmboss->highlightColor());
w.writeUnitFloat("hglO", "#Prc", bevelAndEmboss->highlightOpacity());
w.writeEnum("sdwM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->shadowBlendMode()));
w.writeColor("sdwC", bevelAndEmboss->shadowColor());
w.writeUnitFloat("sdwO", "#Prc", bevelAndEmboss->shadowOpacity());
w.writeEnum("bvlT", "bvlT", techniqueToString(bevelAndEmboss->technique(), "bvlT"));
w.writeEnum("bvlS", "BESl", bevelStyleToString(bevelAndEmboss->style()));
w.writeBoolean("uglg", bevelAndEmboss->useGlobalLight());
w.writeUnitFloat("lagl", "#Ang", bevelAndEmboss->angle());
w.writeUnitFloat("Lald", "#Ang", bevelAndEmboss->altitude());
w.writeUnitFloat("srgR", "#Prc", bevelAndEmboss->depth());
w.writeUnitFloat("blur", "#Pxl", bevelAndEmboss->size());
w.writeEnum("bvlD", "BESs", bevelAndEmboss->direction() == psd_direction_up ? "In " : "Out ");
// FIXME: save curves
w.writeCurve("TrnS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeBoolean("antialiasGloss", bevelAndEmboss->glossAntiAliased());
w.writeUnitFloat("Sftn", "#Pxl", bevelAndEmboss->soften());
if (bevelAndEmboss->contourEnabled()) {
w.writeBoolean("useShape", bevelAndEmboss->contourEnabled());
// FIXME: save curves
w.writeCurve("MpgS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.writeBoolean("AntA", bevelAndEmboss->antiAliased());
w.writeUnitFloat("Inpr", "#Prc", bevelAndEmboss->contourRange());
}
w.writeBoolean("useTexture", bevelAndEmboss->textureEnabled());
if (bevelAndEmboss->textureEnabled()) {
w.writeBoolean("InvT", bevelAndEmboss->textureInvert());
w.writeBoolean("Algn", bevelAndEmboss->textureAlignWithLayer());
w.writeUnitFloat("Scl ", "#Prc", bevelAndEmboss->textureScale());
w.writeUnitFloat("textureDepth ", "#Prc", bevelAndEmboss->textureDepth());
w.writePatternRef("Ptrn", bevelAndEmboss->texturePattern(), fetchPatternUuidSafe(bevelAndEmboss->texturePattern(), patternToUuidMap));
w.writePhasePoint("phase", bevelAndEmboss->texturePhase());
}
w.leaveDescriptor();
}
// Satin
const psd_layer_effects_satin *satin = style->satin();
if (satin->effectEnabled()) {
w.enterDescriptor("ChFX", "", "ChFX");
w.writeBoolean("enab", satin->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(satin->blendMode()));
w.writeColor("Clr ", satin->color());
w.writeBoolean("AntA", satin->antiAliased());
w.writeBoolean("Invr", satin->invert());
w.writeUnitFloat("Opct", "#Prc", satin->opacity());
w.writeUnitFloat("lagl", "#Ang", satin->angle());
w.writeUnitFloat("Dstn", "#Pxl", satin->distance());
w.writeUnitFloat("blur", "#Pxl", satin->size());
// FIXME: save curves
w.writeCurve("MpgS",
"Linear",
QVector<QPointF>() << QPointF() << QPointF(255, 255));
w.leaveDescriptor();
}
const psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
if (colorOverlay->effectEnabled()) {
w.enterDescriptor("SoFi", "", "SoFi");
w.writeBoolean("enab", colorOverlay->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(colorOverlay->blendMode()));
w.writeUnitFloat("Opct", "#Prc", colorOverlay->opacity());
w.writeColor("Clr ", colorOverlay->color());
w.leaveDescriptor();
}
// Gradient Overlay
const psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(gradientOverlay->gradient().data());
KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(gradientOverlay->gradient().data());
if (gradientOverlay->effectEnabled() && (segmentGradient || stopGradient)) {
w.enterDescriptor("GrFl", "", "GrFl");
w.writeBoolean("enab", gradientOverlay->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(gradientOverlay->blendMode()));
w.writeUnitFloat("Opct", "#Prc", gradientOverlay->opacity());
if (segmentGradient) {
w.writeSegmentGradient("Grad", segmentGradient);
} else if (stopGradient) {
w.writeStopGradient("Grad", stopGradient);
}
w.writeUnitFloat("Angl", "#Ang", gradientOverlay->angle());
w.writeEnum("Type", "GrdT", gradientTypeToString(gradientOverlay->style()));
w.writeBoolean("Rvrs", gradientOverlay->reverse());
w.writeBoolean("Algn", gradientOverlay->alignWithLayer());
w.writeUnitFloat("Scl ", "#Prc", gradientOverlay->scale());
w.writeOffsetPoint("Ofst", gradientOverlay->gradientOffset());
// FIXME: Krita doesn't support dithering
w.writeBoolean("Dthr", true/*gradientOverlay->dither()*/);
w.leaveDescriptor();
}
// Pattern Overlay
const psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
if (patternOverlay->effectEnabled()) {
w.enterDescriptor("patternFill", "", "patternFill");
w.writeBoolean("enab", patternOverlay->effectEnabled());
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(patternOverlay->blendMode()));
w.writeUnitFloat("Opct", "#Prc", patternOverlay->opacity());
w.writePatternRef("Ptrn", patternOverlay->pattern(), fetchPatternUuidSafe(patternOverlay->pattern(), patternToUuidMap));
w.writeUnitFloat("Scl ", "#Prc", patternOverlay->scale());
w.writeBoolean("Algn", patternOverlay->alignWithLayer());
w.writePhasePoint("phase", patternOverlay->patternPhase());
w.leaveDescriptor();
}
const psd_layer_effects_stroke *stroke = style->stroke();
if (stroke->effectEnabled()) {
w.enterDescriptor("FrFX", "", "FrFX");
w.writeBoolean("enab", stroke->effectEnabled());
w.writeEnum("Styl", "FStl", strokePositionToString(stroke->position()));
w.writeEnum("PntT", "FrFl", strokeFillTypeToString(stroke->fillType()));
w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(stroke->blendMode()));
w.writeUnitFloat("Opct", "#Prc", stroke->opacity());
w.writeUnitFloat("Sz ", "#Pxl", stroke->size());
if (stroke->fillType() == psd_fill_solid_color) {
w.writeColor("Clr ", stroke->color());
} else if (stroke->fillType() == psd_fill_gradient) {
KoSegmentGradient *segmentGradient = dynamic_cast<KoSegmentGradient*>(stroke->gradient().data());
KoStopGradient *stopGradient = dynamic_cast<KoStopGradient*>(stroke->gradient().data());
if (segmentGradient) {
w.writeSegmentGradient("Grad", segmentGradient);
} else if (stopGradient) {
w.writeStopGradient("Grad", stopGradient);
} else {
- qWarning() << "WARNING: Stroke: Unknown gradient type!";
+ warnKrita << "WARNING: Stroke: Unknown gradient type!";
w.writeColor("Clr ", stroke->color());
}
w.writeUnitFloat("Angl", "#Ang", stroke->angle());
w.writeEnum("Type", "GrdT", gradientTypeToString(stroke->style()));
w.writeBoolean("Rvrs", stroke->reverse());
w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
w.writeBoolean("Algn", stroke->alignWithLayer());
w.writeOffsetPoint("Ofst", stroke->gradientOffset());
// FIXME: Krita doesn't support dithering
w.writeBoolean("Dthr", true/*stroke->dither()*/);
} else if (stroke->fillType() == psd_fill_pattern) {
w.writePatternRef("Ptrn", stroke->pattern(), fetchPatternUuidSafe(stroke->pattern(), patternToUuidMap));
w.writeUnitFloat("Scl ", "#Prc", stroke->scale());
w.writeBoolean("Lnkd", stroke->alignWithLayer());
w.writePhasePoint("phase", stroke->patternPhase());
}
w.leaveDescriptor();
}
w.leaveDescriptor();
w.leaveDescriptor();
}
return w.document();
}
inline QDomNode findNodeByClassId(const QString &classId, QDomNode parent) {
return KisDomUtils::findElementByAttibute(parent, "node", "classId", classId);
}
void replaceAllChildren(QDomNode src, QDomNode dst)
{
QDomNode node;
do {
node = dst.lastChild();
dst.removeChild(node);
} while(!node.isNull());
node = src.firstChild();
while(!node.isNull()) {
dst.appendChild(node);
node = src.firstChild();
}
src.parentNode().removeChild(src);
}
QDomDocument KisAslLayerStyleSerializer::formPsdXmlDocument() const
{
QDomDocument doc = formXmlDocument();
QDomNode nullNode = findNodeByClassId("null", doc.documentElement());
QDomNode stylNode = findNodeByClassId("Styl", doc.documentElement());
QDomNode lefxNode = findNodeByClassId("Lefx", stylNode);
replaceAllChildren(lefxNode, nullNode);
return doc;
}
void KisAslLayerStyleSerializer::saveToDevice(QIODevice *device)
{
QDomDocument doc = formXmlDocument();
if (doc.isNull()) return ;
KisAslWriter writer;
writer.writeFile(device, doc);
}
void convertAndSetBlendMode(const QString &mode,
boost::function<void (const QString &)> setBlendMode)
{
QString compositeOp = COMPOSITE_OVER;
if (mode == "Nrml") {
compositeOp = COMPOSITE_OVER;
} else if (mode == "Dslv") {
compositeOp = COMPOSITE_DISSOLVE;
} else if (mode == "Drkn") {
compositeOp = COMPOSITE_DARKEN;
} else if (mode == "Mltp") {
compositeOp = COMPOSITE_MULT;
} else if (mode == "CBrn") {
compositeOp = COMPOSITE_BURN;
} else if (mode == "linearBurn") {
compositeOp = COMPOSITE_LINEAR_BURN;
} else if (mode == "darkerColor") {
compositeOp = COMPOSITE_DARKER_COLOR;
} else if (mode == "Lghn") {
compositeOp = COMPOSITE_LIGHTEN;
} else if (mode == "Scrn") {
compositeOp = COMPOSITE_SCREEN;
} else if (mode == "CDdg") {
compositeOp = COMPOSITE_DODGE;
} else if (mode == "linearDodge") {
compositeOp = COMPOSITE_LINEAR_DODGE;
} else if (mode == "lighterColor") {
compositeOp = COMPOSITE_LIGHTER_COLOR;
} else if (mode == "Ovrl") {
compositeOp = COMPOSITE_OVERLAY;
} else if (mode == "SftL") {
compositeOp = COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
} else if (mode == "HrdL") {
compositeOp = COMPOSITE_HARD_LIGHT;
} else if (mode == "vividLight") {
compositeOp = COMPOSITE_VIVID_LIGHT;
} else if (mode == "linearLight") {
compositeOp = COMPOSITE_LINEAR_LIGHT;
} else if (mode == "pinLight") {
compositeOp = COMPOSITE_PIN_LIGHT;
} else if (mode == "hardMix") {
compositeOp = COMPOSITE_HARD_MIX;
} else if (mode == "Dfrn") {
compositeOp = COMPOSITE_DIFF;
} else if (mode == "Xclu") {
compositeOp = COMPOSITE_EXCLUSION;
} else if (mode == "Sbtr") {
compositeOp = COMPOSITE_SUBTRACT;
} else if (mode == "divide") {
compositeOp = COMPOSITE_DIVIDE;
} else if (mode == "H ") {
compositeOp = COMPOSITE_HUE;
} else if (mode == "Strt") {
compositeOp = COMPOSITE_SATURATION;
} else if (mode == "Clr ") {
compositeOp = COMPOSITE_COLOR;
} else if (mode == "Lmns") {
compositeOp = COMPOSITE_LUMINIZE;
} else {
- qDebug() << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!";
+ dbgKrita << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!";
}
setBlendMode(compositeOp);
}
void convertAndSetCurve(const QString &name,
const QVector<QPointF> &points,
boost::function<void (const quint8*)> setCurveLookupTable)
{
Q_UNUSED(name);
Q_UNUSED(points);
Q_UNUSED(setCurveLookupTable);
- qWarning() << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet";
+ warnKrita << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet";
}
template <typename T>
void convertAndSetEnum(const QString &value,
const QMap<QString, T> map,
boost::function<void (T)> setMappedValue)
{
setMappedValue(map[value]);
}
inline QString _prepaddr(const QString &pref, const QString &addr) {
return pref + addr;
}
#define CONN_TEXT_RADDR(addr, method, object, type) m_catcher.subscribeText(addr, boost::bind(&type::method, object, _1))
#define CONN_COLOR(addr, method, object, type, prefix) m_catcher.subscribeColor(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1))
#define CONN_UNITF(addr, unit, method, object, type, prefix) m_catcher.subscribeUnitFloat(_prepaddr(prefix, addr), unit, boost::bind(&type::method, object, _1))
#define CONN_BOOL(addr, method, object, type, prefix) m_catcher.subscribeBoolean(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1))
#define CONN_POINT(addr, method, object, type, prefix) m_catcher.subscribePoint(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1))
#define CONN_COMPOSITE_OP(addr, method, object, type, prefix) \
{ \
boost::function<void (const QString&)> setter = \
boost::bind(&type::method, object, _1); \
m_catcher.subscribeEnum(_prepaddr(prefix, addr), "BlnM", boost::bind(convertAndSetBlendMode, _1, setter)); \
}
#define CONN_CURVE(addr, method, object, type, prefix) \
{ \
boost::function<void (const quint8*)> setter = \
boost::bind(&type::method, object, _1); \
m_catcher.subscribeCurve(_prepaddr(prefix, addr), boost::bind(convertAndSetCurve, _1, _2, setter)); \
}
#define CONN_ENUM(addr, tag, method, map, mapped_type, object, type, prefix) \
{ \
boost::function<void (mapped_type)> setter = \
boost::bind(&type::method, object, _1); \
m_catcher.subscribeEnum(_prepaddr(prefix, addr), tag, boost::bind(convertAndSetEnum<mapped_type>, _1, map, setter)); \
}
#define CONN_GRADIENT(addr, method, object, type, prefix) \
{ \
m_catcher.subscribeGradient(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1)); \
}
#define CONN_PATTERN(addr, method, object, type, prefix) \
{ \
boost::function<void (KoPattern*)> setter = \
boost::bind(&type::method, object, _1); \
m_catcher.subscribePatternRef(_prepaddr(prefix, addr), boost::bind(&KisAslLayerStyleSerializer::assignPatternObject, this, _1, _2, setter)); \
}
void KisAslLayerStyleSerializer::registerPatternObject(const KoPattern *pattern) {
QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern);
if (m_patternsStore.contains(uuid)) {
- qWarning() << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[uuid]->name());
+ warnKrita << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[uuid]->name());
} else {
KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
KoPattern *patternToAdd = server->resourceByMD5(pattern->md5());
if (!patternToAdd) {
patternToAdd = pattern->clone();
server->addResource(patternToAdd, false);
}
m_patternsStore.insert(uuid, patternToAdd);
}
}
void KisAslLayerStyleSerializer::assignPatternObject(const QString &patternUuid,
const QString &patternName,
boost::function<void (KoPattern *)> setPattern)
{
Q_UNUSED(patternName);
KoPattern *pattern = m_patternsStore[patternUuid];
if (!pattern) {
- qWarning() << "WARNING: ASL style contains inexistent pattern reference!";
+ warnKrita << "WARNING: ASL style contains inexistent pattern reference!";
QImage dumbImage(32, 32, QImage::Format_ARGB32);
dumbImage.fill(Qt::red);
KoPattern *dumbPattern = new KoPattern(dumbImage, "invalid", "");
registerPatternObject(dumbPattern);
pattern = dumbPattern;
}
setPattern(pattern);
}
class FillStylesCorrector {
public:
static void correct(KisPSDLayerStyle *style) {
correctWithoutPattern(style->outerGlow());
correctWithoutPattern(style->innerGlow());
correctWithPattern(style->stroke());
}
private:
template <class T>
static void correctWithPattern(T *config) {
if (config->pattern()) {
config->setFillType(psd_fill_pattern);
} else if (config->gradient()) {
config->setFillType(psd_fill_gradient);
} else {
config->setFillType(psd_fill_solid_color);
}
}
template <class T>
static void correctWithoutPattern(T *config) {
if (config->gradient()) {
config->setFillType(psd_fill_gradient);
} else {
config->setFillType(psd_fill_solid_color);
}
}
};
void KisAslLayerStyleSerializer::connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix)
{
CONN_TEXT_RADDR("/null/Nm ", setName, style, KisPSDLayerStyle);
CONN_TEXT_RADDR("/null/Idnt", setPsdUuid, style, KisPSDLayerStyle);
CONN_BOOL("/masterFXSwitch", setEnabled, style, KisPSDLayerStyle, prefix);
psd_layer_effects_drop_shadow *dropShadow = style->dropShadow();
CONN_COMPOSITE_OP("/DrSh/Md ", setBlendMode, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_COLOR("/DrSh/Clr ", setColor, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/Opct", "#Prc", setOpacity, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/lagl", "#Ang", setAngle, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/Dstn", "#Pxl", setDistance, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/Ckmt", "#Pxl", setSpread, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/blur", "#Pxl", setSize, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_UNITF("/DrSh/Nose", "#Prc", setNoise, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_BOOL("/DrSh/enab", setEffectEnabled, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_BOOL("/DrSh/uglg", setUseGlobalLight, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_BOOL("/DrSh/AntA", setAntiAliased, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_BOOL("/DrSh/layerConceals", setKnocksOut, dropShadow, psd_layer_effects_drop_shadow, prefix);
CONN_CURVE("/DrSh/TrnS", setContourLookupTable, dropShadow, psd_layer_effects_drop_shadow, prefix);
psd_layer_effects_inner_shadow *innerShadow = style->innerShadow();
CONN_COMPOSITE_OP("/IrSh/Md ", setBlendMode, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_COLOR("/IrSh/Clr ", setColor, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/Opct", "#Prc", setOpacity, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/lagl", "#Ang", setAngle, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/Dstn", "#Pxl", setDistance, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/Ckmt", "#Pxl", setSpread, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/blur", "#Pxl", setSize, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_UNITF("/IrSh/Nose", "#Prc", setNoise, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_BOOL("/IrSh/enab", setEffectEnabled, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_BOOL("/IrSh/uglg", setUseGlobalLight, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_BOOL("/IrSh/AntA", setAntiAliased, innerShadow, psd_layer_effects_inner_shadow, prefix);
CONN_CURVE("/IrSh/TrnS", setContourLookupTable, innerShadow, psd_layer_effects_inner_shadow, prefix);
psd_layer_effects_outer_glow *outerGlow = style->outerGlow();
CONN_COMPOSITE_OP("/OrGl/Md ", setBlendMode, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_COLOR("/OrGl/Clr ", setColor, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/Opct", "#Prc", setOpacity, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/Ckmt", "#Pxl", setSpread, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/blur", "#Pxl", setSize, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/Nose", "#Prc", setNoise, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_BOOL("/OrGl/enab", setEffectEnabled, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_BOOL("/OrGl/AntA", setAntiAliased, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_CURVE("/OrGl/TrnS", setContourLookupTable, outerGlow, psd_layer_effects_outer_glow, prefix);
QMap<QString, psd_technique_type> fillTechniqueMap;
fillTechniqueMap.insert("PrBL", psd_technique_precise);
fillTechniqueMap.insert("SfBL", psd_technique_softer);
CONN_ENUM("/OrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_GRADIENT("/OrGl/Grad", setGradient, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/Inpr", "#Prc", setRange, outerGlow, psd_layer_effects_outer_glow, prefix);
CONN_UNITF("/OrGl/ShdN", "#Prc", setJitter, outerGlow, psd_layer_effects_outer_glow, prefix);
psd_layer_effects_inner_glow *innerGlow = style->innerGlow();
CONN_COMPOSITE_OP("/IrGl/Md ", setBlendMode, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_COLOR("/IrGl/Clr ", setColor, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/Opct", "#Prc", setOpacity, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/Ckmt", "#Pxl", setSpread, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/blur", "#Pxl", setSize, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/Nose", "#Prc", setNoise, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_BOOL("/IrGl/enab", setEffectEnabled, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_BOOL("/IrGl/AntA", setAntiAliased, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_CURVE("/IrGl/TrnS", setContourLookupTable, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_ENUM("/IrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_GRADIENT("/IrGl/Grad", setGradient, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/Inpr", "#Prc", setRange, innerGlow, psd_layer_effects_inner_glow, prefix);
CONN_UNITF("/IrGl/ShdN", "#Prc", setJitter, innerGlow, psd_layer_effects_inner_glow, prefix);
QMap<QString, psd_glow_source> glowSourceMap;
glowSourceMap.insert("SrcC", psd_glow_center);
glowSourceMap.insert("SrcE", psd_glow_edge);
CONN_ENUM("/IrGl/glwS", "IGSr", setSource, glowSourceMap, psd_glow_source, innerGlow, psd_layer_effects_inner_glow, prefix);
psd_layer_effects_satin *satin = style->satin();
CONN_COMPOSITE_OP("/ChFX/Md ", setBlendMode, satin, psd_layer_effects_satin, prefix);
CONN_COLOR("/ChFX/Clr ", setColor, satin, psd_layer_effects_satin, prefix);
CONN_UNITF("/ChFX/Opct", "#Prc", setOpacity, satin, psd_layer_effects_satin, prefix);
CONN_UNITF("/ChFX/lagl", "#Ang", setAngle, satin, psd_layer_effects_satin, prefix);
CONN_UNITF("/ChFX/Dstn", "#Pxl", setDistance, satin, psd_layer_effects_satin, prefix);
CONN_UNITF("/ChFX/blur", "#Pxl", setSize, satin, psd_layer_effects_satin, prefix);
CONN_BOOL("/ChFX/enab", setEffectEnabled, satin, psd_layer_effects_satin, prefix);
CONN_BOOL("/ChFX/AntA", setAntiAliased, satin, psd_layer_effects_satin, prefix);
CONN_BOOL("/ChFX/Invr", setInvert, satin, psd_layer_effects_satin, prefix);
CONN_CURVE("/ChFX/MpgS", setContourLookupTable, satin, psd_layer_effects_satin, prefix);
psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay();
CONN_COMPOSITE_OP("/SoFi/Md ", setBlendMode, colorOverlay, psd_layer_effects_color_overlay, prefix);
CONN_COLOR("/SoFi/Clr ", setColor, colorOverlay, psd_layer_effects_color_overlay, prefix);
CONN_UNITF("/SoFi/Opct", "#Prc", setOpacity, colorOverlay, psd_layer_effects_color_overlay, prefix);
CONN_BOOL("/SoFi/enab", setEffectEnabled, colorOverlay, psd_layer_effects_color_overlay, prefix);
psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay();
CONN_COMPOSITE_OP("/GrFl/Md ", setBlendMode, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_UNITF("/GrFl/Opct", "#Prc", setOpacity, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_UNITF("/GrFl/Scl ", "#Prc", setScale, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_UNITF("/GrFl/Angl", "#Ang", setAngle, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_BOOL("/GrFl/enab", setEffectEnabled, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
// CONN_BOOL("/GrFl/Dthr", setDitherNotImplemented, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_BOOL("/GrFl/Rvrs", setReverse, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_BOOL("/GrFl/Algn", setAlignWithLayer, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_POINT("/GrFl/Ofst", setGradientOffset, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
CONN_GRADIENT("/GrFl/Grad", setGradient, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
QMap<QString, psd_gradient_style> gradientStyleMap;
gradientStyleMap.insert("Lnr ", psd_gradient_style_linear);
gradientStyleMap.insert("Rdl ", psd_gradient_style_radial);
gradientStyleMap.insert("Angl", psd_gradient_style_angle);
gradientStyleMap.insert("Rflc", psd_gradient_style_reflected);
gradientStyleMap.insert("Dmnd", psd_gradient_style_diamond);
CONN_ENUM("/GrFl/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, gradientOverlay, psd_layer_effects_gradient_overlay, prefix);
psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay();
CONN_BOOL("/patternFill/enab", setEffectEnabled, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_COMPOSITE_OP("/patternFill/Md ", setBlendMode, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_UNITF("/patternFill/Opct", "#Prc", setOpacity, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_PATTERN("/patternFill/Ptrn", setPattern, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_UNITF("/patternFill/Scl ", "#Prc", setScale, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_BOOL("/patternFill/Algn", setAlignWithLayer, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
CONN_POINT("/patternFill/phase", setPatternPhase, patternOverlay, psd_layer_effects_pattern_overlay, prefix);
psd_layer_effects_stroke *stroke = style->stroke();
CONN_COMPOSITE_OP("/FrFX/Md ", setBlendMode, stroke, psd_layer_effects_stroke, prefix);
CONN_BOOL("/FrFX/enab", setEffectEnabled, stroke, psd_layer_effects_stroke, prefix);
CONN_UNITF("/FrFX/Opct", "#Prc", setOpacity, stroke, psd_layer_effects_stroke, prefix);
CONN_UNITF("/FrFX/Sz ", "#Pxl", setSize, stroke, psd_layer_effects_stroke, prefix);
QMap<QString, psd_stroke_position> strokeStyleMap;
strokeStyleMap.insert("OutF", psd_stroke_outside);
strokeStyleMap.insert("InsF", psd_stroke_inside);
strokeStyleMap.insert("CtrF", psd_stroke_center);
CONN_ENUM("/FrFX/Styl", "FStl", setPosition, strokeStyleMap, psd_stroke_position, stroke, psd_layer_effects_stroke, prefix);
QMap<QString, psd_fill_type> strokeFillType;
strokeFillType.insert("SClr", psd_fill_solid_color);
strokeFillType.insert("GrFl", psd_fill_gradient);
strokeFillType.insert("Ptrn", psd_fill_pattern);
CONN_ENUM("/FrFX/PntT", "FrFl", setFillType, strokeFillType, psd_fill_type, stroke, psd_layer_effects_stroke, prefix);
// Color type
CONN_COLOR("/FrFX/Clr ", setColor, stroke, psd_layer_effects_stroke, prefix);
// Gradient Type
CONN_GRADIENT("/FrFX/Grad", setGradient, stroke, psd_layer_effects_stroke, prefix);
CONN_UNITF("/FrFX/Angl", "#Ang", setAngle, stroke, psd_layer_effects_stroke, prefix);
CONN_UNITF("/FrFX/Scl ", "#Prc", setScale, stroke, psd_layer_effects_stroke, prefix);
CONN_ENUM("/FrFX/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, stroke, psd_layer_effects_stroke, prefix);
CONN_BOOL("/FrFX/Rvrs", setReverse, stroke, psd_layer_effects_stroke, prefix);
CONN_BOOL("/FrFX/Algn", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix);
CONN_POINT("/FrFX/Ofst", setGradientOffset, stroke, psd_layer_effects_stroke, prefix);
// CONN_BOOL("/FrFX/Dthr", setDitherNotImplemented, stroke, psd_layer_effects_stroke, prefix);
// Pattern type
CONN_PATTERN("/FrFX/Ptrn", setPattern, stroke, psd_layer_effects_stroke, prefix);
CONN_BOOL("/FrFX/Lnkd", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); // yes, we share the params...
CONN_POINT("/FrFX/phase", setPatternPhase, stroke, psd_layer_effects_stroke, prefix);
psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss();
CONN_BOOL("/ebbl/enab", setEffectEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_COMPOSITE_OP("/ebbl/hglM", setHighlightBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_COLOR("/ebbl/hglC", setHighlightColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/hglO", "#Prc", setHighlightOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_COMPOSITE_OP("/ebbl/sdwM", setShadowBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_COLOR("/ebbl/sdwC", setShadowColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/sdwO", "#Prc", setShadowOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
QMap<QString, psd_technique_type> bevelTechniqueMap;
bevelTechniqueMap.insert("PrBL", psd_technique_precise);
bevelTechniqueMap.insert("SfBL", psd_technique_softer);
bevelTechniqueMap.insert("Slmt", psd_technique_slope_limit);
CONN_ENUM("/ebbl/bvlT", "bvlT", setTechnique, bevelTechniqueMap, psd_technique_type, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
QMap<QString, psd_bevel_style> bevelStyleMap;
bevelStyleMap.insert("OtrB", psd_bevel_outer_bevel);
bevelStyleMap.insert("InrB", psd_bevel_inner_bevel);
bevelStyleMap.insert("Embs", psd_bevel_emboss);
bevelStyleMap.insert("PlEb", psd_bevel_pillow_emboss);
bevelStyleMap.insert("strokeEmboss", psd_bevel_stroke_emboss);
CONN_ENUM("/ebbl/bvlS", "BESl", setStyle, bevelStyleMap, psd_bevel_style, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_BOOL("/ebbl/uglg", setUseGlobalLight, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/lagl", "#Ang", setAngle, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/Lald", "#Ang", setAltitude, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/srgR", "#Prc", setDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/blur", "#Pxl", setSize, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
QMap<QString, psd_direction> bevelDirectionMap;
bevelDirectionMap.insert("In ", psd_direction_up);
bevelDirectionMap.insert("Out ", psd_direction_down);
CONN_ENUM("/ebbl/bvlD", "BESs", setDirection, bevelDirectionMap, psd_direction, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_CURVE("/ebbl/TrnS", setContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_BOOL("/ebbl/antialiasGloss", setGlossAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/Sftn", "#Pxl", setSoften, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
// Use shape mode
CONN_BOOL("/ebbl/useShape", setContourEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_CURVE("/ebbl/MpgS", setGlossContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_BOOL("/ebbl/AntA", setAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/Inpr", "#Prc", setContourRange, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
// Use texture mode
CONN_BOOL("/ebbl/useTexture", setTextureEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_BOOL("/ebbl/InvT", setTextureInvert, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_BOOL("/ebbl/Algn", setTextureAlignWithLayer, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/Scl ", "#Prc", setTextureScale, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_UNITF("/ebbl/textureDepth", "#Prc", setTextureDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_PATTERN("/ebbl/Ptrn", setTexturePattern, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
CONN_POINT("/ebbl/phase", setTexturePhase, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix);
}
void KisAslLayerStyleSerializer::newStyleStarted(bool isPsdStructure)
{
m_stylesVector.append(toQShared(new KisPSDLayerStyle()));
KisPSDLayerStyle *currentStyle = m_stylesVector.last().data();
psd_layer_effects_context *context = currentStyle->context();
context->keep_original = 0;
QString prefix = isPsdStructure ? "/null" : "/Styl/Lefx";
connectCatcherToStyle(currentStyle, prefix);
}
void KisAslLayerStyleSerializer::readFromDevice(QIODevice *device)
{
m_stylesVector.clear();
m_catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
m_catcher.subscribeNewStyleStarted(boost::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, false));
KisAslReader reader;
QDomDocument doc = reader.readFile(device);
- //qDebug() << ppVar(doc.toString());
+ //dbgKrita << ppVar(doc.toString());
//KisAslObjectCatcher c2;
KisAslXmlParser parser;
parser.parseXML(doc, m_catcher);
// correct all the layer styles
foreach(KisPSDLayerStyleSP style, m_stylesVector) {
FillStylesCorrector::correct(style.data());
}
}
void KisAslLayerStyleSerializer::registerPSDPattern(const QDomDocument &doc)
{
KisAslCallbackObjectCatcher catcher;
catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
//KisAslObjectCatcher c2;
KisAslXmlParser parser;
parser.parseXML(doc, catcher);
}
void KisAslLayerStyleSerializer::readFromPSDXML(const QDomDocument &doc)
{
// The caller prepares the document using th efollowing code
//
// KisAslReader reader;
// QDomDocument doc = reader.readLfx2PsdSection(device);
m_stylesVector.clear();
//m_catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1));
m_catcher.subscribeNewStyleStarted(boost::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, true));
//KisAslObjectCatcher c2;
KisAslXmlParser parser;
parser.parseXML(doc, m_catcher);
// correct all the layer styles
foreach(KisPSDLayerStyleSP style, m_stylesVector) {
FillStylesCorrector::correct(style.data());
}
}
diff --git a/krita/ui/kis_config.cc b/krita/ui/kis_config.cc
index 5c97ff2748d..468f6e1d157 100644
--- a/krita/ui/kis_config.cc
+++ b/krita/ui/kis_config.cc
@@ -1,1604 +1,1604 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_config.h"
#include <limits.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <QFont>
#include <QThread>
#include <QStringList>
#include <kglobal.h>
#include <kconfig.h>
#include <KisDocument.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <kis_debug.h>
#include "kis_canvas_resource_provider.h"
#include "kis_global.h"
#include "kis_config_notifier.h"
#include <config-ocio.h>
#include <kis_color_manager.h>
KisConfig::KisConfig()
: m_cfg(KGlobal::config()->group(""))
{
}
KisConfig::~KisConfig()
{
if (qApp->thread() != QThread::currentThread()) {
- //qDebug() << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Skipping...";
+ //dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Skipping...";
return;
}
m_cfg.sync();
}
bool KisConfig::disableTouchOnCanvas(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false));
}
void KisConfig::setDisableTouchOnCanvas(bool value) const
{
m_cfg.writeEntry("disableTouchOnCanvas", value);
}
bool KisConfig::useProjections(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useProjections", true));
}
void KisConfig::setUseProjections(bool useProj) const
{
m_cfg.writeEntry("useProjections", useProj);
}
bool KisConfig::undoEnabled(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true));
}
void KisConfig::setUndoEnabled(bool undo) const
{
m_cfg.writeEntry("undoEnabled", undo);
}
int KisConfig::undoStackLimit(bool defaultValue) const
{
return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30));
}
void KisConfig::setUndoStackLimit(int limit) const
{
m_cfg.writeEntry("undoStackLimit", limit);
}
bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false));
}
void KisConfig::setCumulativeUndoRedo(bool value)
{
m_cfg.writeEntry("useCumulativeUndoRedo", value);
}
double KisConfig::stackT1(bool defaultValue) const
{
return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5));
}
void KisConfig::setStackT1(int T1)
{
m_cfg.writeEntry("stackT1", T1);
}
double KisConfig::stackT2(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1));
}
void KisConfig::setStackT2(int T2)
{
m_cfg.writeEntry("stackT2", T2);
}
int KisConfig::stackN(bool defaultValue) const
{
return (defaultValue ? 5 : m_cfg.readEntry("stackN",5));
}
void KisConfig::setStackN(int N)
{
m_cfg.writeEntry("stackN", N);
}
qint32 KisConfig::defImageWidth(bool defaultValue) const
{
return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600));
}
qint32 KisConfig::defImageHeight(bool defaultValue) const
{
return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200));
}
double KisConfig::defImageResolution(bool defaultValue) const
{
return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0;
}
QString KisConfig::defColorModel(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id()
: m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id()));
}
void KisConfig::defColorModel(const QString & model) const
{
m_cfg.writeEntry("colorModelDef", model);
}
QString KisConfig::defaultColorDepth(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id()
: m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id()));
}
void KisConfig::setDefaultColorDepth(const QString & depth) const
{
m_cfg.writeEntry("colorDepthDef", depth);
}
QString KisConfig::defColorProfile(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() :
m_cfg.readEntry("colorProfileDef",
KoColorSpaceRegistry::instance()->rgb8()->profile()->name()));
}
void KisConfig::defColorProfile(const QString & profile) const
{
m_cfg.writeEntry("colorProfileDef", profile);
}
void KisConfig::defImageWidth(qint32 width) const
{
m_cfg.writeEntry("imageWidthDef", width);
}
void KisConfig::defImageHeight(qint32 height) const
{
m_cfg.writeEntry("imageHeightDef", height);
}
void KisConfig::defImageResolution(double res) const
{
m_cfg.writeEntry("imageResolutionDef", res*72.0);
}
void cleanOldCursorStyleKeys(KConfigGroup &cfg)
{
if (cfg.hasKey("newCursorStyle") &&
cfg.hasKey("newOutlineStyle")) {
cfg.deleteEntry("cursorStyleDef");
}
}
CursorStyle KisConfig::newCursorStyle(bool defaultValue) const
{
if (defaultValue) {
return CURSOR_STYLE_NO_CURSOR;
}
int style = m_cfg.readEntry("newCursorStyle", int(-1));
if (style < 0) {
// old style format
style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE));
switch (style) {
case OLD_CURSOR_STYLE_TOOLICON:
style = CURSOR_STYLE_TOOLICON;
break;
case OLD_CURSOR_STYLE_CROSSHAIR:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS:
style = CURSOR_STYLE_CROSSHAIR;
break;
case OLD_CURSOR_STYLE_POINTER:
style = CURSOR_STYLE_POINTER;
break;
case OLD_CURSOR_STYLE_OUTLINE:
case OLD_CURSOR_STYLE_NO_CURSOR:
style = CURSOR_STYLE_NO_CURSOR;
break;
case OLD_CURSOR_STYLE_SMALL_ROUND:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT:
style = CURSOR_STYLE_SMALL_ROUND;
break;
case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED:
style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED;
break;
case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED:
style = CURSOR_STYLE_TRIANGLE_LEFTHANDED;
break;
default:
style = -1;
}
}
cleanOldCursorStyleKeys(m_cfg);
// compatibility with future versions
if (style < 0 || style >= N_CURSOR_STYLE_SIZE) {
style = CURSOR_STYLE_NO_CURSOR;
}
return (CursorStyle) style;
}
void KisConfig::setNewCursorStyle(CursorStyle style)
{
m_cfg.writeEntry("newCursorStyle", (int)style);
}
OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const
{
if (defaultValue) {
return OUTLINE_FULL;
}
int style = m_cfg.readEntry("newOutlineStyle", int(-1));
if (style < 0) {
// old style format
style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE));
switch (style) {
case OLD_CURSOR_STYLE_TOOLICON:
case OLD_CURSOR_STYLE_CROSSHAIR:
case OLD_CURSOR_STYLE_POINTER:
case OLD_CURSOR_STYLE_NO_CURSOR:
case OLD_CURSOR_STYLE_SMALL_ROUND:
case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED:
style = OUTLINE_NONE;
break;
case OLD_CURSOR_STYLE_OUTLINE:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED:
style = OUTLINE_FULL;
break;
default:
style = -1;
}
}
cleanOldCursorStyleKeys(m_cfg);
// compatibility with future versions
if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) {
style = OUTLINE_FULL;
}
return (OutlineStyle) style;
}
void KisConfig::setNewOutlineStyle(OutlineStyle style)
{
m_cfg.writeEntry("newOutlineStyle", (int)style);
}
QRect KisConfig::colorPreviewRect() const
{
return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect();
}
void KisConfig::setColorPreviewRect(const QRect &rect)
{
m_cfg.writeEntry("colorPreviewRect", QVariant(rect));
}
bool KisConfig::useDirtyPresets(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false));
}
void KisConfig::setUseDirtyPresets(bool value)
{
m_cfg.writeEntry("useDirtyPresets",value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::useEraserBrushSize(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false));
}
void KisConfig::setUseEraserBrushSize(bool value)
{
m_cfg.writeEntry("useEraserBrushSize",value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const
{
QColor col(77, 77, 77);
return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col));
}
void KisConfig::setMDIBackgroundColor(const QColor &v) const
{
m_cfg.writeEntry("mdiBackgroundColor", v);
}
QString KisConfig::getMDIBackgroundImage(bool defaultValue) const
{
return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", ""));
}
void KisConfig::setMDIBackgroundImage(const QString &filename) const
{
m_cfg.writeEntry("mdiBackgroundImage", filename);
}
QString KisConfig::monitorProfile(int screen) const
{
// Note: keep this in sync with the default profile for the RGB colorspaces!
QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc");
- //qDebug() << "KisConfig::monitorProfile()" << profile;
+ //dbgKrita << "KisConfig::monitorProfile()" << profile;
return profile;
}
QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const
{
return (defaultValue ? defaultMonitor
: m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor));
}
void KisConfig::setMonitorForScreen(int screen, const QString& monitor)
{
m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor);
}
void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const
{
m_cfg.writeEntry("monitorProfile/OverrideX11", override);
m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile);
}
const KoColorProfile *KisConfig::getScreenProfile(int screen)
{
KisConfig cfg;
QString monitorId;
if (KisColorManager::instance()->devices().size() > screen) {
monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]);
}
- //qDebug() << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId;
+ //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId;
if (monitorId.isEmpty()) {
return 0;
}
QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId);
- //qDebug() << "\tgetScreenProfile()" << bytes.size();
+ //dbgKrita << "\tgetScreenProfile()" << bytes.size();
if (bytes.length() > 0) {
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes);
- //qDebug() << "\tKisConfig::getScreenProfile for screen" << screen << profile->name();
+ //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name();
return profile;
}
else {
- //qDebug() << "\tCould not get a system monitor profile";
+ //dbgKrita << "\tCould not get a system monitor profile";
return 0;
}
}
const KoColorProfile *KisConfig::displayProfile(int screen) const
{
// if the user plays with the settings, they can override the display profile, in which case
// we don't want the system setting.
bool override = useSystemMonitorProfile();
- //qDebug() << "KisConfig::displayProfile(). Override X11:" << override;
+ //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override;
const KoColorProfile *profile = 0;
if (override) {
- //qDebug() << "\tGoing to get the screen profile";
+ //dbgKrita << "\tGoing to get the screen profile";
profile = KisConfig::getScreenProfile(screen);
}
// if it fails. check the configuration
if (!profile || !profile->isSuitableForDisplay()) {
- //qDebug() << "\tGoing to get the monitor profile";
+ //dbgKrita << "\tGoing to get the monitor profile";
QString monitorProfileName = monitorProfile(screen);
- //qDebug() << "\t\tmonitorProfileName:" << monitorProfileName;
+ //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName;
if (!monitorProfileName.isEmpty()) {
profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName);
}
if (profile) {
- //qDebug() << "\t\tsuitable for display" << profile->isSuitableForDisplay();
+ //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay();
}
else {
- //qDebug() << "\t\tstill no profile";
+ //dbgKrita << "\t\tstill no profile";
}
}
// if we still don't have a profile, or the profile isn't suitable for display,
// we need to get a last-resort profile. the built-in sRGB is a good choice then.
if (!profile || !profile->isSuitableForDisplay()) {
- //qDebug() << "\tnothing worked, going to get sRGB built-in";
+ //dbgKrita << "\tnothing worked, going to get sRGB built-in";
profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in");
}
if (profile) {
- //qDebug() << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name();
+ //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name();
}
else {
- //qDebug() << "\tCouldn't get a display profile at all";
+ //dbgKrita << "\tCouldn't get a display profile at all";
}
return profile;
}
QString KisConfig::workingColorSpace(bool defaultValue) const
{
return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA"));
}
void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const
{
m_cfg.writeEntry("workingColorSpace", workingColorSpace);
}
QString KisConfig::printerColorSpace(bool /*defaultValue*/) const
{
//TODO currently only rgb8 is supported
//return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA"));
return QString("RGBA");
}
void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const
{
m_cfg.writeEntry("printerColorSpace", printerColorSpace);
}
QString KisConfig::printerProfile(bool defaultValue) const
{
return (defaultValue ? "" : m_cfg.readEntry("printerProfile", ""));
}
void KisConfig::setPrinterProfile(const QString & printerProfile) const
{
m_cfg.writeEntry("printerProfile", printerProfile);
}
bool KisConfig::useBlackPointCompensation(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true));
}
void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const
{
m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation);
}
bool KisConfig::allowLCMSOptimization(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true));
}
void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization)
{
m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization);
}
bool KisConfig::showRulers(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showrulers", false));
}
void KisConfig::setShowRulers(bool rulers) const
{
m_cfg.writeEntry("showrulers", rulers);
}
qint32 KisConfig::pasteBehaviour(bool defaultValue) const
{
return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2));
}
void KisConfig::setPasteBehaviour(qint32 renderIntent) const
{
m_cfg.writeEntry("pasteBehaviour", renderIntent);
}
qint32 KisConfig::monitorRenderIntent(bool defaultValue) const
{
qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL);
if (intent > 3) intent = 3;
if (intent < 0) intent = 0;
return (defaultValue ? INTENT_PERCEPTUAL : intent);
}
void KisConfig::setRenderIntent(qint32 renderIntent) const
{
if (renderIntent > 3) renderIntent = 3;
if (renderIntent < 0) renderIntent = 0;
m_cfg.writeEntry("renderIntent", renderIntent);
}
bool KisConfig::useOpenGL(bool defaultValue) const
{
if (qApp->applicationName() == "krita") {
if (defaultValue) {
#ifdef Q_WS_MAC
return false;
#else
return true;
#endif
}
- //qDebug() << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
+ //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
#ifdef Q_WS_MAC
return (m_cfg.readEntry("useOpenGL", false) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL"));
#else
return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL"));
#endif
}
else if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") {
return true; // for sketch and gemini
} else {
return false;
}
}
void KisConfig::setUseOpenGL(bool useOpenGL) const
{
m_cfg.writeEntry("useOpenGL", useOpenGL);
}
int KisConfig::openGLFilteringMode(bool defaultValue) const
{
return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3));
}
void KisConfig::setOpenGLFilteringMode(int filteringMode)
{
m_cfg.writeEntry("OpenGLFilterMode", filteringMode);
}
bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true));
}
void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer)
{
m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer);
}
int KisConfig::openGLTextureSize(bool defaultValue) const
{
return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256));
}
bool KisConfig::disableDoubleBuffering(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("disableDoubleBuffering", true));
}
void KisConfig::setDisableDoubleBuffering(bool disableDoubleBuffering)
{
m_cfg.writeEntry("disableDoubleBuffering", disableDoubleBuffering);
}
bool KisConfig::disableVSync(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("disableVSync", true));
}
void KisConfig::setDisableVSync(bool disableVSync)
{
m_cfg.writeEntry("disableVSync", disableVSync);
}
bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false));
}
bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false));
}
int KisConfig::numMipmapLevels(bool defaultValue) const
{
return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4));
}
int KisConfig::textureOverlapBorder() const
{
return 1 << qMax(0, numMipmapLevels());
}
qint32 KisConfig::maxNumberOfThreads(bool defaultValue) const
{
return (defaultValue ? QThread::idealThreadCount() : m_cfg.readEntry("maxthreads", QThread::idealThreadCount()));
}
void KisConfig::setMaxNumberOfThreads(qint32 maxThreads)
{
m_cfg.writeEntry("maxthreads", maxThreads);
}
quint32 KisConfig::getGridMainStyle(bool defaultValue) const
{
quint32 v = m_cfg.readEntry("gridmainstyle", 0);
if (v > 2)
v = 2;
return (defaultValue ? 0 : v);
}
void KisConfig::setGridMainStyle(quint32 v) const
{
m_cfg.writeEntry("gridmainstyle", v);
}
quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const
{
quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1);
if (v > 2) v = 2;
return (defaultValue ? 1 : v);
}
void KisConfig::setGridSubdivisionStyle(quint32 v) const
{
m_cfg.writeEntry("gridsubdivisionstyle", v);
}
QColor KisConfig::getGridMainColor(bool defaultValue) const
{
QColor col(99, 99, 99);
return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col));
}
void KisConfig::setGridMainColor(const QColor & v) const
{
m_cfg.writeEntry("gridmaincolor", v);
}
QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const
{
QColor col(150, 150, 150);
return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col));
}
void KisConfig::setGridSubdivisionColor(const QColor & v) const
{
m_cfg.writeEntry("gridsubdivisioncolor", v);
}
quint32 KisConfig::getGridHSpacing(bool defaultValue) const
{
qint32 v = m_cfg.readEntry("gridhspacing", 10);
return (defaultValue ? 10 : (quint32)qMax(1, v));
}
void KisConfig::setGridHSpacing(quint32 v) const
{
m_cfg.writeEntry("gridhspacing", v);
}
quint32 KisConfig::getGridVSpacing(bool defaultValue) const
{
qint32 v = m_cfg.readEntry("gridvspacing", 10);
return (defaultValue ? 10 : (quint32)qMax(1, v));
}
void KisConfig::setGridVSpacing(quint32 v) const
{
m_cfg.writeEntry("gridvspacing", v);
}
bool KisConfig::getGridSpacingAspect(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("gridspacingaspect", false));
}
void KisConfig::setGridSpacingAspect(bool v) const
{
m_cfg.writeEntry("gridspacingaspect", v);
}
quint32 KisConfig::getGridSubdivisions(bool defaultValue) const
{
qint32 v = m_cfg.readEntry("gridsubsivisons", 2);
return (defaultValue ? 2 : (quint32)qMax(1, v));
}
void KisConfig::setGridSubdivisions(quint32 v) const
{
m_cfg.writeEntry("gridsubsivisons", v);
}
quint32 KisConfig::getGridOffsetX(bool defaultValue) const
{
qint32 v = m_cfg.readEntry("gridoffsetx", 0);
return (defaultValue ? 0 : (quint32)qMax(0, v));
}
void KisConfig::setGridOffsetX(quint32 v) const
{
m_cfg.writeEntry("gridoffsetx", v);
}
quint32 KisConfig::getGridOffsetY(bool defaultValue) const
{
qint32 v = m_cfg.readEntry("gridoffsety", 0);
return (defaultValue ? 0 : (quint32)qMax(0, v));
}
void KisConfig::setGridOffsetY(quint32 v) const
{
m_cfg.writeEntry("gridoffsety", v);
}
bool KisConfig::getGridOffsetAspect(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("gridoffsetaspect", false));
}
void KisConfig::setGridOffsetAspect(bool v) const
{
m_cfg.writeEntry("gridoffsetaspect", v);
}
qint32 KisConfig::checkSize(bool defaultValue) const
{
return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32));
}
void KisConfig::setCheckSize(qint32 checksize) const
{
m_cfg.writeEntry("checksize", checksize);
}
bool KisConfig::scrollCheckers(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false));
}
void KisConfig::setScrollingCheckers(bool sc) const
{
m_cfg.writeEntry("scrollingcheckers", sc);
}
QColor KisConfig::canvasBorderColor(bool defaultValue) const
{
QColor color(QColor(128,128,128));
return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color));
}
void KisConfig::setCanvasBorderColor(const QColor& color) const
{
m_cfg.writeEntry("canvasBorderColor", color);
}
bool KisConfig::hideScrollbars(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false));
}
void KisConfig::setHideScrollbars(bool value) const
{
m_cfg.writeEntry("hideScrollbars", value);
}
QColor KisConfig::checkersColor1(bool defaultValue) const
{
QColor col(220, 220, 220);
return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col));
}
void KisConfig::setCheckersColor1(const QColor & v) const
{
m_cfg.writeEntry("checkerscolor", v);
}
QColor KisConfig::checkersColor2(bool defaultValue) const
{
return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white)));
}
void KisConfig::setCheckersColor2(const QColor & v) const
{
m_cfg.writeEntry("checkerscolor2", v);
}
bool KisConfig::antialiasCurves(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true));
}
void KisConfig::setAntialiasCurves(bool v) const
{
m_cfg.writeEntry("antialiascurves", v);
}
QColor KisConfig::selectionOverlayMaskColor(bool defaultValue) const
{
QColor def(255, 0, 0, 220);
return (defaultValue ? def : m_cfg.readEntry("selectionOverlayMaskColor", def));
}
void KisConfig::setSelectionOverlayMaskColor(const QColor &color)
{
m_cfg.writeEntry("selectionOverlayMaskColor", color);
}
bool KisConfig::antialiasSelectionOutline(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false));
}
void KisConfig::setAntialiasSelectionOutline(bool v) const
{
m_cfg.writeEntry("AntialiasSelectionOutline", v);
}
bool KisConfig::showRootLayer(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false));
}
void KisConfig::setShowRootLayer(bool showRootLayer) const
{
m_cfg.writeEntry("ShowRootLayer", showRootLayer);
}
bool KisConfig::showGlobalSelection(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false));
}
void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const
{
m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection);
}
bool KisConfig::showOutlineWhilePainting(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true));
}
void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const
{
m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting);
}
bool KisConfig::hideSplashScreen(bool defaultValue) const
{
KConfigGroup cfg(KGlobal::config(), "SplashScreen");
return (defaultValue ? true : cfg.readEntry("HideSplashAfterStartup", true));
}
void KisConfig::setHideSplashScreen(bool hideSplashScreen) const
{
KConfigGroup cfg(KGlobal::config(), "SplashScreen");
cfg.writeEntry("HideSplashAfterStartup", hideSplashScreen);
}
qreal KisConfig::outlineSizeMinimum(bool defaultValue) const
{
return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0));
}
void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const
{
m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum);
}
int KisConfig::autoSaveInterval(bool defaultValue) const
{
return (defaultValue ? KisDocument::defaultAutoSave() : m_cfg.readEntry("AutoSaveInterval", KisDocument::defaultAutoSave()));
}
void KisConfig::setAutoSaveInterval(int seconds) const
{
return m_cfg.writeEntry("AutoSaveInterval", seconds);
}
bool KisConfig::backupFile(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true));
}
void KisConfig::setBackupFile(bool backupFile) const
{
m_cfg.writeEntry("CreateBackupFile", backupFile);
}
bool KisConfig::showFilterGallery(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false));
}
void KisConfig::setShowFilterGallery(bool showFilterGallery) const
{
m_cfg.writeEntry("showFilterGallery", showFilterGallery);
}
bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true));
}
void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const
{
m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery);
}
QString KisConfig::canvasState(bool defaultValue) const
{
return (defaultValue ? "OPENGL_NOT_TRIED" : m_cfg.readEntry("canvasState", "OPENGL_NOT_TRIED"));
}
void KisConfig::setCanvasState(const QString& state) const
{
static QStringList acceptableStates;
if (acceptableStates.isEmpty()) {
acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED";
}
if (acceptableStates.contains(state)) {
m_cfg.writeEntry("canvasState", state);
m_cfg.sync();
}
}
bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false));
}
void KisConfig::setToolOptionsPopupDetached(bool detached) const
{
m_cfg.writeEntry("ToolOptionsPopupDetached", detached);
}
bool KisConfig::paintopPopupDetached(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false));
}
void KisConfig::setPaintopPopupDetached(bool detached) const
{
m_cfg.writeEntry("PaintopPopupDetached", detached);
}
QString KisConfig::pressureTabletCurve(bool defaultValue) const
{
return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;"));
}
void KisConfig::setPressureTabletCurve(const QString& curveString) const
{
m_cfg.writeEntry("tabletPressureCurve", curveString);
}
qreal KisConfig::vastScrolling(bool defaultValue) const
{
return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9));
}
void KisConfig::setVastScrolling(const qreal factor) const
{
m_cfg.writeEntry("vastScrolling", factor);
}
int KisConfig::presetChooserViewMode(bool defaultValue) const
{
return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0));
}
void KisConfig::setPresetChooserViewMode(const int mode) const
{
m_cfg.writeEntry("presetChooserViewMode", mode);
}
bool KisConfig::firstRun(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("firstRun", true));
}
void KisConfig::setFirstRun(const bool first) const
{
m_cfg.writeEntry("firstRun", first);
}
int KisConfig::horizontalSplitLines(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1));
}
void KisConfig::setHorizontalSplitLines(const int numberLines) const
{
m_cfg.writeEntry("horizontalSplitLines", numberLines);
}
int KisConfig::verticalSplitLines(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1));
}
void KisConfig::setVerticalSplitLines(const int numberLines) const
{
m_cfg.writeEntry("verticalSplitLines", numberLines);
}
bool KisConfig::clicklessSpacePan(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true));
}
void KisConfig::setClicklessSpacePan(const bool toggle) const
{
m_cfg.writeEntry("clicklessSpacePan", toggle);
}
bool KisConfig::hideDockersFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true));
}
void KisConfig::setHideDockersFullscreen(const bool value) const
{
m_cfg.writeEntry("hideDockersFullScreen", value);
}
bool KisConfig::showDockerTitleBars(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showDockerTitleBars", true));
}
void KisConfig::setShowDockerTitleBars(const bool value) const
{
m_cfg.writeEntry("showDockerTitleBars", value);
}
bool KisConfig::hideMenuFullscreen(bool defaultValue) const
{
return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true));
}
void KisConfig::setHideMenuFullscreen(const bool value) const
{
m_cfg.writeEntry("hideMenuFullScreen", value);
}
bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true));
}
void KisConfig::setHideScrollbarsFullscreen(const bool value) const
{
m_cfg.writeEntry("hideScrollbarsFullScreen", value);
}
bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const
{
return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true));
}
void KisConfig::setHideStatusbarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideStatusbarFullScreen", value);
}
bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true));
}
void KisConfig::setHideTitlebarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideTitleBarFullscreen", value);
}
bool KisConfig::hideToolbarFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true));
}
void KisConfig::setHideToolbarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideToolbarFullscreen", value);
}
bool KisConfig::fullscreenMode(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true));
}
void KisConfig::setFullscreenMode(const bool value) const
{
m_cfg.writeEntry("fullscreenMode", value);
}
QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const
{
return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList()));
}
void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const
{
m_cfg.writeEntry("favoriteCompositeOps", compositeOps);
}
QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString()));
}
void KisConfig::setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const
{
QString exportConfig = properties.toXML();
m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig);
}
bool KisConfig::useOcio(bool defaultValue) const
{
#ifdef HAVE_OCIO
return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false));
#else
return false;
#endif
}
void KisConfig::setUseOcio(bool useOCIO) const
{
m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO);
}
int KisConfig::favoritePresets(bool defaultValue) const
{
return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10));
}
void KisConfig::setFavoritePresets(const int value)
{
m_cfg.writeEntry("numFavoritePresets", value);
}
KisConfig::OcioColorManagementMode
KisConfig::ocioColorManagementMode(bool defaultValue) const
{
return (OcioColorManagementMode)(defaultValue ? INTERNAL
: m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL));
}
void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const
{
m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode);
}
QString KisConfig::ocioConfigurationPath(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString()));
}
void KisConfig::setOcioConfigurationPath(const QString &path) const
{
m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path);
}
QString KisConfig::ocioLutPath(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString()));
}
void KisConfig::setOcioLutPath(const QString &path) const
{
m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path);
}
int KisConfig::ocioLutEdgeSize(bool defaultValue) const
{
return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64));
}
void KisConfig::setOcioLutEdgeSize(int value)
{
m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value);
}
bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false));
}
void KisConfig::setOcioLockColorVisualRepresentation(bool value)
{
m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value);
}
QString KisConfig::defaultPalette(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", QString()));
}
void KisConfig::setDefaultPalette(const QString& name) const
{
m_cfg.writeEntry("defaultPalette", name);
}
QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const
{
QString def = "flow";
if (sliderNumber == 1) {
def = "opacity";
}
if (sliderNumber == 2) {
def = "size";
}
return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def));
}
void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider)
{
m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider);
}
bool KisConfig::sliderLabels(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true));
}
void KisConfig::setSliderLabels(bool enabled)
{
m_cfg.writeEntry("sliderLabels", enabled);
}
QString KisConfig::currentInputProfile(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString()));
}
void KisConfig::setCurrentInputProfile(const QString& name)
{
m_cfg.writeEntry("currentInputProfile", name);
}
bool KisConfig::useSystemMonitorProfile(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false));
}
void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const
{
m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile);
}
bool KisConfig::presetStripVisible(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true));
}
void KisConfig::setPresetStripVisible(bool visible)
{
m_cfg.writeEntry("presetStripVisible", visible);
}
bool KisConfig::scratchpadVisible(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true));
}
void KisConfig::setScratchpadVisible(bool visible)
{
m_cfg.writeEntry("scratchpadVisible", visible);
}
bool KisConfig::showSingleChannelAsColor(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false));
}
void KisConfig::setShowSingleChannelAsColor(bool asColor)
{
m_cfg.writeEntry("showSingleChannelAsColor", asColor);
}
bool KisConfig::hidePopups(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("hidePopups", false));
}
void KisConfig::setHidePopups(bool hidepopups)
{
m_cfg.writeEntry("hidePopups", hidepopups);
}
int KisConfig::numDefaultLayers(bool defaultValue) const
{
return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2));
}
void KisConfig::setNumDefaultLayers(int num)
{
m_cfg.writeEntry("NumberOfLayersForNewImage", num);
}
quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const
{
return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8));
}
void KisConfig::setDefaultBackgroundOpacity(quint8 value)
{
m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value);
}
QColor KisConfig::defaultBackgroundColor(bool defaultValue) const
{
return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white)));
}
void KisConfig::setDefaultBackgroundColor(QColor value)
{
m_cfg.writeEntry("BackgroundColorForNewImage", value);
}
KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const
{
return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER));
}
void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value)
{
m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value);
}
int KisConfig::lineSmoothingType(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1));
}
void KisConfig::setLineSmoothingType(int value)
{
m_cfg.writeEntry("LineSmoothingType", value);
}
qreal KisConfig::lineSmoothingDistance(bool defaultValue) const
{
return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0));
}
void KisConfig::setLineSmoothingDistance(qreal value)
{
m_cfg.writeEntry("LineSmoothingDistance", value);
}
qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const
{
return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15));
}
void KisConfig::setLineSmoothingTailAggressiveness(qreal value)
{
m_cfg.writeEntry("LineSmoothingTailAggressiveness", value);
}
bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false));
}
void KisConfig::setLineSmoothingSmoothPressure(bool value)
{
m_cfg.writeEntry("LineSmoothingSmoothPressure", value);
}
bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true));
}
void KisConfig::setLineSmoothingScalableDistance(bool value)
{
m_cfg.writeEntry("LineSmoothingScalableDistance", value);
}
qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const
{
return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0));
}
void KisConfig::setLineSmoothingDelayDistance(qreal value)
{
m_cfg.writeEntry("LineSmoothingDelayDistance", value);
}
bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true));
}
void KisConfig::setLineSmoothingUseDelayDistance(bool value)
{
m_cfg.writeEntry("LineSmoothingUseDelayDistance", value);
}
bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true));
}
void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value)
{
m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value);
}
bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true));
}
void KisConfig::setLineSmoothingStabilizeSensors(bool value)
{
m_cfg.writeEntry("LineSmoothingStabilizeSensors", value);
}
int KisConfig::paletteDockerPaletteViewSectionSize(bool defaultValue) const
{
return (defaultValue ? 12 : m_cfg.readEntry("paletteDockerPaletteViewSectionSize", 12));
}
void KisConfig::setPaletteDockerPaletteViewSectionSize(int value) const
{
m_cfg.writeEntry("paletteDockerPaletteViewSectionSize", value);
}
int KisConfig::tabletEventsDelay(bool defaultValue) const
{
return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10));
}
void KisConfig::setTabletEventsDelay(int value)
{
m_cfg.writeEntry("tabletEventsDelay", value);
}
bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false));
}
void KisConfig::setTestingAcceptCompressedTabletEvents(bool value)
{
m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value);
}
bool KisConfig::testingCompressBrushEvents(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false));
}
void KisConfig::setTestingCompressBrushEvents(bool value)
{
m_cfg.writeEntry("testingCompressBrushEvents", value);
}
bool KisConfig::useVerboseOpenGLDebugOutput(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useVerboseOpenGLDebugOutput", false));
}
int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const
{
return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0));
}
bool KisConfig::showCanvasMessages(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true));
}
void KisConfig::setShowCanvasMessages(bool show)
{
m_cfg.writeEntry("showOnCanvasMessages", show);
}
bool KisConfig::compressKra(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false));
}
void KisConfig::setCompressKra(bool compress)
{
m_cfg.writeEntry("compressLayersInKra", compress);
}
bool KisConfig::toolOptionsInDocker(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true));
}
void KisConfig::setToolOptionsInDocker(bool inDocker)
{
m_cfg.writeEntry("ToolOptionsInDocker", inDocker);
}
const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const
{
const KoColorSpace *cs = 0;
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) {
KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance();
cs = csr->colorSpace(cfg.readEntry("customColorSpaceModel", "RGBA"),
cfg.readEntry("customColorSpaceDepthID", "U8"),
cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)"));
}
return cs;
}
void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs)
{
KConfigGroup cfg = KGlobal::config()->group("advancedColorSelector");
cfg.writeEntry("useCustomColorSpace", bool(cs));
if(cs) {
cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id());
cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id());
cfg.writeEntry("customColorSpaceProfile", cs->profile()->name());
}
KisConfigNotifier::instance()->notifyConfigChanged();
}
diff --git a/krita/ui/kis_factory2.cc b/krita/ui/kis_factory2.cc
index 2e9e9e276c9..10bb29a96da 100644
--- a/krita/ui/kis_factory2.cc
+++ b/krita/ui/kis_factory2.cc
@@ -1,97 +1,78 @@
/*
* kis_factory.cc - part of Krayon
*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//#ifdef _MSC_VER // this removes KDEWIN extensions to stdint.h: required by exiv2
//#define KDEWIN_STDINT_H
//#endif
#include "kis_factory2.h"
#include <QStringList>
#include <QDir>
#include <kcomponentdata.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <kglobal.h>
-#include <KoHashGeneratorProvider.h>
-
#include <kis_debug.h>
#include "kis_aboutdata.h"
#include "KisPart.h"
-#include "kis_md5_generator.h"
-
K4AboutData* KisFactory::s_aboutData = 0;
KComponentData* KisFactory::s_componentData = 0;
KisFactory::KisFactory()
{
(void)componentData();
- KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
}
KisFactory::~KisFactory()
{
delete s_aboutData;
s_aboutData = 0;
delete s_componentData;
s_componentData = 0;
}
K4AboutData* KisFactory::aboutData()
{
if (!s_aboutData) {
s_aboutData = newKritaAboutData();
}
return s_aboutData;
}
const KComponentData &KisFactory::componentData()
{
if (!s_componentData) {
s_componentData = new KComponentData(aboutData());
Q_CHECK_PTR(s_componentData);
- // for cursors
- KGlobal::dirs()->addResourceType("kis_pics", "data", "krita/pics/");
-
- // for images in the paintop box
- KGlobal::dirs()->addResourceType("kis_images", "data", "krita/images/");
-
- KGlobal::dirs()->addResourceType("icc_profiles", "data", "krita/profiles/");
-
- // Tell the iconloader about share/apps/calligra/icons
- KIconLoader::global()->addAppDir("calligra");
}
return *s_componentData;
}
-const QString KisFactory::componentName()
-{
- return "krita";
-}
diff --git a/krita/ui/kis_factory2.h b/krita/ui/kis_factory2.h
index a61447cdad2..61c94b36966 100644
--- a/krita/ui/kis_factory2.h
+++ b/krita/ui/kis_factory2.h
@@ -1,47 +1,46 @@
/*
* kis_factory2.h - part of Krayon
*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_FACTORY_2_H_
#define KIS_FACTORY_2_H_
#include <QStringList>
#include <kritaui_export.h>
#include <kcomponentdata.h>
#include <kaboutdata.h>
class KRITAUI_EXPORT KisFactory
{
public:
KisFactory();
~KisFactory();
static K4AboutData *aboutData();
static const KComponentData &componentData();
- static const QString componentName();
private:
static KComponentData *s_componentData;
static K4AboutData *s_aboutData;
};
#endif
diff --git a/krita/ui/kis_favorite_resource_manager.cpp b/krita/ui/kis_favorite_resource_manager.cpp
index 826dd77c91e..2ddfe666b66 100644
--- a/krita/ui/kis_favorite_resource_manager.cpp
+++ b/krita/ui/kis_favorite_resource_manager.cpp
@@ -1,369 +1,369 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
Copyright 2011 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
-#include <QDebug>
+#include <kis_debug.h>
#include <QPoint>
#include <QStringList>
#include <QString>
#include <QColor>
#include <kis_paintop_registry.h>
#include <kis_paintop_preset.h>
#include <KoID.h>
#include <kconfig.h>
#include "kis_favorite_resource_manager.h"
#include "kis_popup_palette.h"
#include "kis_paintop_box.h"
#include "KisViewManager.h"
#include "kis_resource_server_provider.h"
#include "kis_min_heap.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
class KisFavoriteResourceManager::ColorDataList
{
public:
static const int MAX_RECENT_COLOR = 12;
ColorDataList() {
m_key = 0;
}
~ColorDataList() {
}
int size() {
return m_guiList.size();
}
int leastUsedGuiPos() {
return findPos(m_priorityList.valueAt(0));
}
const KoColor& guiColor(int pos) {
Q_ASSERT_X(pos < size(), "ColorDataList::guiColor", "index out of bound");
Q_ASSERT_X(pos >= 0, "ColorDataList::guiColor", "negative index");
return m_guiList.at(pos)->data;
}
void append(const KoColor& data) {
int pos = findPos(data);
if (pos > -1) updateKey(pos);
else appendNew(data);
}
void appendNew(const KoColor& data) {
if (size() >= ColorDataList::MAX_RECENT_COLOR) removeLeastUsed();
PriorityNode<KoColor> * node;
node = new PriorityNode <KoColor>();
node->data = data;
node->key = m_key++;
m_priorityList.append(node);
int pos = guiInsertPos(data);
pos >= m_guiList.size() ? m_guiList.append(node)
: m_guiList.insert(pos, node);
node = 0;
}
void removeLeastUsed() {
Q_ASSERT_X(size() >= 0, "ColorDataList::removeLeastUsed", "index out of bound");
if (size() <= 0) return;
int pos = findPos(m_priorityList.valueAt(0));
m_guiList.removeAt(pos);
m_priorityList.remove(0);
}
void updateKey(int guiPos) {
if (m_guiList.at(guiPos)->key == m_key - 1) return;
m_priorityList.changeKey(m_guiList.at(guiPos)->pos, m_key++);
}
/*find position of the color on the gui list*/
int findPos(const KoColor& color) {
int low = 0, high = size(), mid = 0;
while (low < high) {
mid = (low + high) / 2;
if (hsvComparison(color, m_guiList.at(mid)->data) == 0) return mid;
else if (hsvComparison(color, m_guiList.at(mid)->data) < 0) high = mid;
else low = mid + 1;
}
return -1;
}
private:
int m_key;
int guiInsertPos(const KoColor& color) {
int low = 0, high = size() - 1, mid = (low + high) / 2;
while (low < high) {
hsvComparison(color, m_guiList[mid]->data) == -1 ? high = mid
: low = mid + 1;
mid = (low + high) / 2;
}
if (m_guiList.size() > 0) {
if (hsvComparison(color, m_guiList[mid]->data) == 1) ++mid;
}
return mid;
}
/*compares c1 and c2 based on HSV.
c1 < c2, returns -1
c1 = c2, returns 0
c1 > c2, returns 1 */
int hsvComparison(const KoColor& c1, const KoColor& c2) {
QColor qc1 = c1.toQColor();
QColor qc2 = c2.toQColor();
if (qc1.hue() < qc2.hue()) return -1;
if (qc1.hue() > qc2.hue()) return 1;
// hue is the same, ok let's compare saturation
if (qc1.saturation() < qc2.saturation()) return -1;
if (qc1.saturation() > qc2.saturation()) return 1;
// oh, also saturation is same?
if (qc1.value() < qc2.value()) return -1;
if (qc1.value() > qc2.value()) return 1;
// user selected two similar colors
return 0;
}
KisMinHeap <KoColor, MAX_RECENT_COLOR> m_priorityList;
QList <PriorityNode <KoColor>*> m_guiList;
};
KisFavoriteResourceManager::KisFavoriteResourceManager(KisPaintopBox *paintopBox)
: m_paintopBox(paintopBox)
, m_colorList(0)
, m_blockUpdates(false)
, m_initialized(false)
{
KisConfig cfg;
m_maxPresets = cfg.favoritePresets();
m_colorList = new ColorDataList();
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(configChanged()));
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(false);
rServer->addObserver(this);
}
KisFavoriteResourceManager::~KisFavoriteResourceManager()
{
KisConfig cfg;
cfg.writeEntry<QString>("favoritePresetsTag", m_currentTag);
KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
rServer->removeObserver(this);
delete m_colorList;
}
void KisFavoriteResourceManager::unsetResourceServer()
{
// ...
}
QVector<KisPaintOpPresetSP> KisFavoriteResourceManager::favoritePresetList()
{
init();
return m_favoritePresetsList;
}
QList<QImage> KisFavoriteResourceManager::favoritePresetImages()
{
init();
QList<QImage> images;
foreach(KisPaintOpPresetSP preset, m_favoritePresetsList) {
if (preset) {
images.append(preset->image());
}
}
return images;
}
void KisFavoriteResourceManager::setCurrentTag(const QString& tagName)
{
m_currentTag = tagName;
updateFavoritePresets();
}
void KisFavoriteResourceManager::slotChangeActivePaintop(int pos)
{
if (pos < 0 || pos >= m_favoritePresetsList.size()) return;
KoResource* resource = const_cast<KisPaintOpPreset*>(m_favoritePresetsList.at(pos).data());
m_paintopBox->resourceSelected(resource);
emit hidePalettes();
}
int KisFavoriteResourceManager::numFavoritePresets()
{
init();
return m_favoritePresetsList.size();
}
//Recent Colors
void KisFavoriteResourceManager::slotUpdateRecentColor(int pos)
{
// Do not update the key, the colour might be selected but it is not used yet. So we are not supposed
// to update the colour priority when we select it.
m_colorList->updateKey(pos);
emit setSelectedColor(pos);
emit sigSetFGColor(m_colorList->guiColor(pos));
emit hidePalettes();
}
void KisFavoriteResourceManager::slotAddRecentColor(const KoColor& color)
{
m_colorList->append(color);
int pos = m_colorList->findPos(color);
emit setSelectedColor(pos);
}
void KisFavoriteResourceManager::slotChangeFGColorSelector(KoColor c)
{
emit sigChangeFGColorSelector(c);
}
void KisFavoriteResourceManager::removingResource(PointerType resource)
{
if (m_blockUpdates) {
return;
}
if (m_favoritePresetsList.contains(resource.data())) {
updateFavoritePresets();
}
}
void KisFavoriteResourceManager::resourceAdded(PointerType /*resource*/)
{
if (m_blockUpdates) {
return;
}
updateFavoritePresets();
}
void KisFavoriteResourceManager::resourceChanged(PointerType /*resource*/)
{
}
void KisFavoriteResourceManager::setBlockUpdates(bool block)
{
m_blockUpdates = block;
if (!block) {
updateFavoritePresets();
}
}
void KisFavoriteResourceManager::syncTaggedResourceView() {
if (m_blockUpdates) {
return;
}
updateFavoritePresets();
}
void KisFavoriteResourceManager::syncTagAddition(const QString& /*tag*/) {}
void KisFavoriteResourceManager::syncTagRemoval(const QString& /*tag*/) {}
int KisFavoriteResourceManager::recentColorsTotal()
{
return m_colorList->size();
}
const KoColor& KisFavoriteResourceManager::recentColorAt(int pos)
{
return m_colorList->guiColor(pos);
}
void KisFavoriteResourceManager::slotSetBGColor(const KoColor c)
{
m_bgColor = c;
}
KoColor KisFavoriteResourceManager::bgColor() const
{
return m_bgColor;
}
bool sortPresetByName(KisPaintOpPresetSP preset1, KisPaintOpPresetSP preset2)
{
return preset1->name() < preset2->name();
}
void KisFavoriteResourceManager::updateFavoritePresets()
{
m_favoritePresetsList.clear();
KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(false);
QStringList presetFilenames = rServer->searchTag(m_currentTag);
for(int i = 0; i < qMin(m_maxPresets, presetFilenames.size()); i++) {
KisPaintOpPresetSP pr = rServer->resourceByFilename(presetFilenames.at(i));
m_favoritePresetsList.append(pr.data());
qSort(m_favoritePresetsList.begin(), m_favoritePresetsList.end(), sortPresetByName);
}
emit updatePalettes();
}
void KisFavoriteResourceManager::configChanged()
{
KisConfig cfg;
m_maxPresets = cfg.favoritePresets();
updateFavoritePresets();
}
void KisFavoriteResourceManager::init()
{
if (!m_initialized) {
m_initialized = true;
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(true);
KConfigGroup group(KGlobal::config(), "favoriteList");
QStringList oldFavoritePresets = (group.readEntry("favoritePresets")).split(',', QString::SkipEmptyParts);
KisConfig cfg;
m_currentTag = cfg.readEntry<QString>("favoritePresetsTag", QString());
if (!oldFavoritePresets.isEmpty() && m_currentTag.isEmpty()) {
m_currentTag = i18n("Favorite Presets");
foreach( const QString& name, oldFavoritePresets) {
KisPaintOpPresetSP preset = rServer->resourceByName(name);
rServer->addTag(preset.data(), m_currentTag);
}
rServer->tagCategoryAdded(m_currentTag);
cfg.writeEntry<QString>("favoritePresets", QString());
}
updateFavoritePresets();
}
}
diff --git a/krita/ui/kis_file_layer.cpp b/krita/ui/kis_file_layer.cpp
index 5d204d6f05a..f7c7464a67f 100644
--- a/krita/ui/kis_file_layer.cpp
+++ b/krita/ui/kis_file_layer.cpp
@@ -1,194 +1,194 @@
/*
* Copyright (c) 2013 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_file_layer.h"
#include <QFile>
#include <QFileInfo>
#include "kis_transform_worker.h"
#include "kis_filter_strategy.h"
#include "kis_node_progress_proxy.h"
#include "kis_node_visitor.h"
#include "kis_image.h"
#include "commands_new/kis_node_move_command2.h"
KisFileLayer::KisFileLayer(KisImageWSP image, const QString &basePath, const QString &filename, ScalingMethod scaleToImageResolution, const QString &name, quint8 opacity)
: KisExternalLayer(image, name, opacity)
, m_basePath(basePath)
, m_filename(filename)
, m_scalingMethod(scaleToImageResolution)
{
/**
* Set default paint device for a layer. It will be used is case
* the file does not exist anymore. Or course, this can happen only
* in the failing execution path.
*/
m_image = new KisPaintDevice(image->colorSpace());
connect(&m_loader, SIGNAL(loadingFinished(KisImageSP)), SLOT(slotLoadingFinished(KisImageSP)));
QFileInfo fi(path());
if (fi.exists()) {
m_loader.setPath(path());
m_loader.reloadImage();
}
}
KisFileLayer::~KisFileLayer()
{
}
KisFileLayer::KisFileLayer(const KisFileLayer &rhs)
: KisExternalLayer(rhs)
{
m_basePath = rhs.m_basePath;
m_filename = rhs.m_filename;
Q_ASSERT(QFile::exists(rhs.path()));
m_scalingMethod = rhs.m_scalingMethod;
m_image = new KisPaintDevice(rhs.image()->colorSpace());
connect(&m_loader, SIGNAL(loadingFinished(KisImageSP)), SLOT(slotLoadingFinished(KisImageSP)));
m_loader.setPath(path());
m_loader.reloadImage();
}
void KisFileLayer::resetCache()
{
m_loader.reloadImage();
}
const KoColorSpace *KisFileLayer::colorSpace() const
{
return m_image->colorSpace();
}
KisPaintDeviceSP KisFileLayer::original() const
{
return m_image;
}
KisPaintDeviceSP KisFileLayer::paintDevice() const
{
return 0;
}
KisDocumentSectionModel::PropertyList KisFileLayer::sectionModelProperties() const
{
KisDocumentSectionModel::PropertyList l = KisLayer::sectionModelProperties();
l << KisDocumentSectionModel::Property(i18n("File"), m_filename);
return l;
}
void KisFileLayer::setFileName(const QString &basePath, const QString &filename)
{
m_basePath = basePath;
m_filename = filename;
m_loader.setPath(path());
m_loader.reloadImage();
}
QString KisFileLayer::fileName() const
{
return m_filename;
}
QString KisFileLayer::path() const
{
if (m_basePath.isEmpty()) {
return m_filename;
}
else {
return m_basePath + '/' + m_filename;
}
}
KisFileLayer::ScalingMethod KisFileLayer::scalingMethod() const
{
return m_scalingMethod;
}
void KisFileLayer::slotLoadingFinished(KisImageSP importedImage)
{
qint32 oldX = x();
qint32 oldY = y();
m_image->makeCloneFrom(importedImage->projection(), importedImage->projection()->extent());
m_image->setDefaultBounds(new KisDefaultBounds(image()));
if (m_scalingMethod == ToImagePPI && (image()->xRes() != importedImage->xRes()
|| image()->yRes() != importedImage->yRes())) {
qreal xscale = image()->xRes() / importedImage->xRes();
qreal yscale = image()->yRes() / importedImage->yRes();
KisTransformWorker worker(m_image, xscale, yscale, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, KisFilterStrategyRegistry::instance()->get("Bicubic"));
worker.run();
}
else if (m_scalingMethod == ToImageSize) {
QSize sz = importedImage->size();
sz.scale(image()->size(), Qt::KeepAspectRatio);
qreal xscale = (qreal)sz.width() / (qreal)importedImage->width();
qreal yscale = (qreal)sz.height() / (qreal)importedImage->height();
KisTransformWorker worker(m_image, xscale, yscale, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, KisFilterStrategyRegistry::instance()->get("Bicubic"));
worker.run();
}
m_image->setX(oldX);
m_image->setY(oldY);
setDirty();
}
KisNodeSP KisFileLayer::clone() const
{
return KisNodeSP(new KisFileLayer(*this));
}
bool KisFileLayer::allowAsChild(KisNodeSP node) const
{
return node->inherits("KisMask");
}
bool KisFileLayer::accept(KisNodeVisitor& visitor)
{
return visitor.visit(this);
}
void KisFileLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
{
return visitor.visit(this, undoAdapter);
}
KUndo2Command* KisFileLayer::crop(const QRect & rect)
{
QPoint oldPos(x(), y());
QPoint newPos = oldPos - rect.topLeft();
return new KisNodeMoveCommand2(this, oldPos, newPos);
}
KUndo2Command* KisFileLayer::transform(const QTransform &/*transform*/)
{
- qWarning() << "WARNING: File Layer does not support transformations!" << name();
+ warnKrita << "WARNING: File Layer does not support transformations!" << name();
return 0;
}
diff --git a/krita/ui/kis_filter_manager.cc b/krita/ui/kis_filter_manager.cc
index 616bf794a1d..c053e1be606 100644
--- a/krita/ui/kis_filter_manager.cc
+++ b/krita/ui/kis_filter_manager.cc
@@ -1,340 +1,340 @@
/*
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_filter_manager.h"
#include <QHash>
#include <QSignalMapper>
#include <QMessageBox>
#include <kactionmenu.h>
#include <kactioncollection.h>
#include <KoID.h>
#include <KisMainWindow.h>
// krita/image
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include <kis_paint_device.h>
// krita/ui
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include <kis_bookmarked_configuration_manager.h>
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_canvas_resource_provider.h"
#include "dialogs/kis_dlg_filter.h"
#include "strokes/kis_filter_stroke_strategy.h"
#include "krita_utils.h"
struct KisFilterManager::Private {
Private()
: reapplyAction(0)
, actionCollection(0)
, actionManager(0)
, view(0)
{
}
KisAction* reapplyAction;
QHash<QString, KActionMenu*> filterActionMenus;
QHash<KisFilter*, KAction*> filters2Action;
KActionCollection *actionCollection;
KisActionManager *actionManager;
KisViewManager *view;
KisSafeFilterConfigurationSP lastConfiguration;
KisSafeFilterConfigurationSP currentlyAppliedConfiguration;
KisStrokeId currentStrokeId;
QSignalMapper actionsMapper;
QPointer<KisDlgFilter> filterDialog;
};
KisFilterManager::KisFilterManager(KisViewManager * view)
: d(new Private)
{
d->view = view;
}
KisFilterManager::~KisFilterManager()
{
delete d;
}
void KisFilterManager::setView(QPointer<KisView>imageView)
{
Q_UNUSED(imageView);
}
void KisFilterManager::setup(KActionCollection * ac, KisActionManager *actionManager)
{
d->actionCollection = ac;
d->actionManager = actionManager;
// Setup reapply action
d->reapplyAction = new KisAction(i18n("Apply Filter Again"), this);
d->reapplyAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F));
d->actionManager->addAction("filter_apply_again", d->reapplyAction);
d->reapplyAction->setEnabled(false);
connect(d->reapplyAction, SIGNAL(triggered()), SLOT(reapplyLastFilter()));
connect(&d->actionsMapper, SIGNAL(mapped(const QString&)), SLOT(showFilterDialog(const QString&)));
// Setup list of filters
foreach (const QString &filterName, KisFilterRegistry::instance()->keys()) {
insertFilter(filterName);
}
connect(KisFilterRegistry::instance(), SIGNAL(filterAdded(QString)), SLOT(insertFilter(const QString &)));
}
void KisFilterManager::insertFilter(const QString & filterName)
{
Q_ASSERT(d->actionCollection);
KisFilterSP filter = KisFilterRegistry::instance()->value(filterName);
Q_ASSERT(filter);
if (d->filters2Action.keys().contains(filter.data())) {
warnKrita << "Filter" << filterName << " has already been inserted";
return;
}
KoID category = filter->menuCategory();
KActionMenu* actionMenu = d->filterActionMenus[ category.id()];
if (!actionMenu) {
actionMenu = new KActionMenu(category.name(), this);
d->actionCollection->addAction(category.id(), actionMenu);
d->filterActionMenus[category.id()] = actionMenu;
}
KisAction *action = new KisAction(filter->menuEntry(), this);
action->setShortcut(filter->shortcut(), KAction::DefaultShortcut);
action->setActivationFlags(KisAction::ACTIVE_DEVICE);
d->actionManager->addAction(QString("krita_filter_%1").arg(filterName), action);
d->filters2Action[filter.data()] = action;
actionMenu->addAction(action);
d->actionsMapper.setMapping(action, filterName);
connect(action, SIGNAL(triggered()), &d->actionsMapper, SLOT(map()));
}
void KisFilterManager::updateGUI()
{
if (!d->view) return;
bool enable = false;
KisNodeSP activeNode = d->view->activeNode();
enable = activeNode && activeNode->hasEditablePaintDevice();
d->reapplyAction->setEnabled(enable);
for (QHash<KisFilter*, KAction*>::iterator it = d->filters2Action.begin();
it != d->filters2Action.end(); ++it) {
bool localEnable = enable;
it.value()->setEnabled(localEnable);
}
}
void KisFilterManager::reapplyLastFilter()
{
if (!d->lastConfiguration) return;
apply(d->lastConfiguration);
finish();
}
void KisFilterManager::showFilterDialog(const QString &filterId)
{
if (d->filterDialog && d->filterDialog->isVisible()) {
KisFilterSP filter = KisFilterRegistry::instance()->value(filterId);
d->filterDialog->setFilter(filter);
return;
}
connect(d->view->image(),
SIGNAL(sigStrokeCancellationRequested()),
SLOT(slotStrokeCancelRequested()),
Qt::UniqueConnection);
connect(d->view->image(),
SIGNAL(sigStrokeEndRequested()),
SLOT(slotStrokeEndRequested()),
Qt::UniqueConnection);
/**
* The UI should show only after every running stroke is finished,
* so the barrier is added here.
*/
d->view->image()->barrierLock();
d->view->image()->unlock();
Q_ASSERT(d->view);
Q_ASSERT(d->view->activeNode());
KisPaintDeviceSP dev = d->view->activeNode()->paintDevice();
if (!dev) {
- qWarning() << "KisFilterManager::showFilterDialog(): Filtering was requested for illegal active layer!" << d->view->activeNode();
+ warnKrita << "KisFilterManager::showFilterDialog(): Filtering was requested for illegal active layer!" << d->view->activeNode();
return;
}
KisFilterSP filter = KisFilterRegistry::instance()->value(filterId);
if (dev->colorSpace()->willDegrade(filter->colorSpaceIndependence())) {
// Warning bells!
if (filter->colorSpaceIndependence() == TO_LAB16) {
if (QMessageBox::warning(d->view->mainWindow(),
i18nc("@title:window", "Krita"),
i18n("The %1 filter will convert your %2 data to 16-bit L*a*b* and vice versa. ",
filter->name(),
dev->colorSpace()->name()),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
!= QMessageBox::Ok) return;
} else if (filter->colorSpaceIndependence() == TO_RGBA16) {
if (QMessageBox::warning(d->view->mainWindow(),
i18nc("@title:window", "Krita"),
i18n("The %1 filter will convert your %2 data to 16-bit RGBA and vice versa. ",
filter->name() , dev->colorSpace()->name()),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
!= QMessageBox::Ok) return;
}
}
if (filter->showConfigurationWidget()) {
if (!d->filterDialog) {
d->filterDialog = new KisDlgFilter(d->view , d->view->activeNode(), this, d->view->mainWindow());
d->filterDialog->setAttribute(Qt::WA_DeleteOnClose);
}
d->filterDialog->setFilter(filter);
d->filterDialog->setVisible(true);
} else {
apply(KisSafeFilterConfigurationSP(filter->defaultConfiguration(d->view->activeNode()->original())));
finish();
}
}
void KisFilterManager::apply(KisSafeFilterConfigurationSP filterConfig)
{
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
KisImageWSP image = d->view->image();
if (d->currentStrokeId) {
image->addJob(d->currentStrokeId, new KisFilterStrokeStrategy::CancelSilentlyMarker);
image->cancelStroke(d->currentStrokeId);
d->currentStrokeId.clear();
}
KisPostExecutionUndoAdapter *undoAdapter =
image->postExecutionUndoAdapter();
KoCanvasResourceManager *resourceManager =
d->view->resourceProvider()->resourceManager();
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image,
d->view->activeNode(),
undoAdapter,
resourceManager);
d->currentStrokeId =
image->startStroke(new KisFilterStrokeStrategy(filter,
KisSafeFilterConfigurationSP(filterConfig),
resources));
QRect processRect = d->view->activeNode()->exactBounds();
processRect = filter->changedRect(processRect, filterConfig.data());
if (filter->supportsThreading()) {
QSize size = KritaUtils::optimalPatchSize();
QVector<QRect> rects = KritaUtils::splitRectIntoPatches(processRect, size);
foreach(const QRect &rc, rects) {
image->addJob(d->currentStrokeId,
new KisFilterStrokeStrategy::Data(rc, true));
}
} else {
image->addJob(d->currentStrokeId,
new KisFilterStrokeStrategy::Data(processRect, false));
}
d->currentlyAppliedConfiguration = filterConfig;
}
void KisFilterManager::finish()
{
Q_ASSERT(d->currentStrokeId);
d->view->image()->endStroke(d->currentStrokeId);
KisFilterSP filter = KisFilterRegistry::instance()->value(d->currentlyAppliedConfiguration->name());
if (filter->bookmarkManager()) {
filter->bookmarkManager()->save(KisBookmarkedConfigurationManager::ConfigLastUsed,
d->currentlyAppliedConfiguration.data());
}
d->lastConfiguration = d->currentlyAppliedConfiguration;
d->reapplyAction->setEnabled(true);
d->reapplyAction->setText(i18n("Apply Filter Again: %1", filter->name()));
d->currentStrokeId.clear();
d->currentlyAppliedConfiguration.clear();
}
void KisFilterManager::cancel()
{
Q_ASSERT(d->currentStrokeId);
d->view->image()->cancelStroke(d->currentStrokeId);
d->currentStrokeId.clear();
d->currentlyAppliedConfiguration.clear();
}
bool KisFilterManager::isStrokeRunning() const
{
return d->currentStrokeId;
}
void KisFilterManager::slotStrokeEndRequested()
{
if (d->currentStrokeId && d->filterDialog) {
d->filterDialog->accept();
}
}
void KisFilterManager::slotStrokeCancelRequested()
{
if (d->currentStrokeId && d->filterDialog) {
d->filterDialog->reject();
}
}
diff --git a/krita/ui/kis_import_catcher.cc b/krita/ui/kis_import_catcher.cc
index 77590d243c6..c20ddfc8ce3 100644
--- a/krita/ui/kis_import_catcher.cc
+++ b/krita/ui/kis_import_catcher.cc
@@ -1,130 +1,130 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_import_catcher.h"
#include <kis_debug.h>
#include <klocale.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kurl.h>
#include <KisImportExportManager.h>
#include "kis_node_manager.h"
#include "kis_types.h"
#include "kis_count_visitor.h"
#include "KisViewManager.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_node_commands_adapter.h"
#include "kis_group_layer.h"
#include "kis_statusbar.h"
#include "kis_progress_widget.h"
#include "KisPart.h"
struct KisImportCatcher::Private
{
public:
KisDocument* doc;
KisViewManager* view;
KUrl url;
QString layerType;
QString prettyLayerName() const;
void importAsPaintLayer(KisPaintDeviceSP device);
void importAsTransparencyMask(KisPaintDeviceSP device);
};
QString KisImportCatcher::Private::prettyLayerName() const
{
QString name = url.fileName();
return !name.isEmpty() ? name : url.prettyUrl();
}
void KisImportCatcher::Private::importAsPaintLayer(KisPaintDeviceSP device)
{
KisLayerSP newLayer =
new KisPaintLayer(view->image(),
prettyLayerName(),
OPACITY_OPAQUE_U8,
device);
KisNodeSP parent = 0;
KisLayerSP currentActiveLayer = view->activeLayer();
if (currentActiveLayer) {
parent = currentActiveLayer->parent();
}
if (parent.isNull()) {
parent = view->image()->rootLayer();
}
KisNodeCommandsAdapter adapter(view);
adapter.addNode(newLayer, parent, currentActiveLayer);
}
KisImportCatcher::KisImportCatcher(const KUrl & url, KisViewManager * view, const QString &layerType)
: m_d(new Private)
{
m_d->doc = KisPart::instance()->createDocument();
KoProgressProxy *progressProxy = view->statusBar()->progress()->progressProxy();
m_d->doc->setProgressProxy(progressProxy);
m_d->view = view;
m_d->url = url;
m_d->layerType = layerType;
connect(m_d->doc, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished()));
bool result = m_d->doc->openUrl(url);
if (!result) {
deleteMyself();
}
}
void KisImportCatcher::slotLoadingFinished()
{
KisImageWSP importedImage = m_d->doc->image();
importedImage->waitForDone();
if (importedImage && importedImage->projection()->exactBounds().isValid()) {
if (m_d->layerType != "KisPaintLayer") {
m_d->view->nodeManager()->createNode(m_d->layerType, false, importedImage->projection());
} else {
m_d->importAsPaintLayer(importedImage->projection());
}
}
deleteMyself();
}
void KisImportCatcher::deleteMyself()
{
m_d->doc->deleteLater();
deleteLater();
}
KisImportCatcher::~KisImportCatcher()
{
delete m_d;
}
diff --git a/krita/ui/kis_layer_manager.cc b/krita/ui/kis_layer_manager.cc
index 61d1c940bef..2984dbd4138 100644
--- a/krita/ui/kis_layer_manager.cc
+++ b/krita/ui/kis_layer_manager.cc
@@ -1,1005 +1,1005 @@
/*
* Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_manager.h"
#include <QRect>
#include <QApplication>
#include <QCursor>
#include <QString>
#include <QDialog>
#include <QVBoxLayout>
#include <QFileInfo>
#include <QDesktopServices>
#include <kactioncollection.h>
#include <klocale.h>
#include <QMessageBox>
#include <kfilewidget.h>
#include <kurl.h>
#include <kdiroperator.h>
#include <kurlcombobox.h>
#include <kmimetype.h>
#include <KoIcon.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include <KoPointerEvent.h>
#include <KoColorProfile.h>
#include <KoSelection.h>
#include <KisPart.h>
#include <KisMainWindow.h>
#include <filter/kis_filter_configuration.h>
#include <filter/kis_filter.h>
#include <kis_filter_strategy.h>
#include <generator/kis_generator_layer.h>
#include <kis_file_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_mask.h>
#include <kis_clone_layer.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <kis_selection.h>
#include <flake/kis_shape_layer.h>
#include <kis_undo_adapter.h>
#include <kis_painter.h>
#include <metadata/kis_meta_data_store.h>
#include <metadata/kis_meta_data_merge_strategy_registry.h>
#include <kis_psd_layer_style.h>
#include "KisView.h"
#include "kis_config.h"
#include "kis_cursor.h"
#include "dialogs/kis_dlg_adj_layer_props.h"
#include "dialogs/kis_dlg_adjustment_layer.h"
#include "dialogs/kis_dlg_layer_properties.h"
#include "dialogs/kis_dlg_generator_layer.h"
#include "dialogs/kis_dlg_file_layer.h"
#include "dialogs/kis_dlg_layer_style.h"
#include "KisDocument.h"
#include "kis_filter_manager.h"
#include "kis_node_visitor.h"
#include "kis_paint_layer.h"
#include "commands/kis_image_commands.h"
#include "commands/kis_layer_commands.h"
#include "commands/kis_node_commands.h"
#include "kis_canvas_resource_provider.h"
#include "kis_selection_manager.h"
#include "kis_statusbar.h"
#include "KisViewManager.h"
#include "kis_zoom_manager.h"
#include "canvas/kis_canvas2.h"
#include "widgets/kis_meta_data_merge_strategy_chooser_widget.h"
#include "widgets/kis_wdg_generator.h"
#include "kis_progress_widget.h"
#include "kis_node_commands_adapter.h"
#include "kis_node_manager.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "KisPart.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_abstract_projection_plane.h"
#include "commands_new/kis_set_layer_style_command.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_selection_mask.h"
class KisSaveGroupVisitor : public KisNodeVisitor
{
public:
KisSaveGroupVisitor(KisImageWSP image,
bool saveInvisible,
bool saveTopLevelOnly,
const KUrl &url,
const QString &baseName,
const QString &extension,
const QString &mimeFilter)
: m_image(image)
, m_saveInvisible(saveInvisible)
, m_saveTopLevelOnly(saveTopLevelOnly)
, m_url(url)
, m_baseName(baseName)
, m_extension(extension)
, m_mimeFilter(mimeFilter)
{
}
virtual ~KisSaveGroupVisitor()
{
}
public:
bool visit(KisNode* ) {
return true;
}
bool visit(KisPaintLayer *) {
return true;
}
bool visit(KisAdjustmentLayer *) {
return true;
}
bool visit(KisExternalLayer *) {
return true;
}
bool visit(KisCloneLayer *) {
return true;
}
bool visit(KisFilterMask *) {
return true;
}
bool visit(KisTransformMask *) {
return true;
}
bool visit(KisTransparencyMask *) {
return true;
}
bool visit(KisGeneratorLayer * ) {
return true;
}
bool visit(KisSelectionMask* ) {
return true;
}
bool visit(KisGroupLayer *layer)
{
if (layer == m_image->rootLayer()) {
KisLayerSP child = dynamic_cast<KisLayer*>(layer->firstChild().data());
while (child) {
child->accept(*this);
child = dynamic_cast<KisLayer*>(child->nextSibling().data());
}
}
else if (layer->visible() || m_saveInvisible) {
QRect r = m_image->bounds();
KisDocument *d = KisPart::instance()->createDocument();
d->prepareForImport();
KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name());
dst->setResolution(m_image->xRes(), m_image->yRes());
d->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", layer->opacity());
KisPainter gc(paintLayer->paintDevice());
gc.bitBlt(QPoint(0, 0), layer->projection(), r);
dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
dst->refreshGraph();
d->setOutputMimeType(m_mimeFilter.toLatin1());
d->setSaveInBatchMode(true);
KUrl url = m_url;
url.adjustPath(KUrl::AddTrailingSlash);
url.setFileName(m_baseName + '_' + layer->name().replace(' ', '_') + '.' + m_extension);
d->exportDocument(url);
if (!m_saveTopLevelOnly) {
KisGroupLayerSP child = dynamic_cast<KisGroupLayer*>(layer->firstChild().data());
while (child) {
child->accept(*this);
child = dynamic_cast<KisGroupLayer*>(child->nextSibling().data());
}
}
delete d;
}
return true;
}
private:
KisImageWSP m_image;
bool m_saveInvisible;
bool m_saveTopLevelOnly;
KUrl m_url;
QString m_baseName;
QString m_extension;
QString m_mimeFilter;
};
KisLayerManager::KisLayerManager(KisViewManager * view)
: m_view(view)
, m_imageView(0)
, m_imageFlatten(0)
, m_imageMergeLayer(0)
, m_groupLayersSave(0)
, m_imageResizeToLayer(0)
, m_flattenLayer(0)
, m_rasterizeLayer(0)
, m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
, m_layerStyle(0)
{
}
KisLayerManager::~KisLayerManager()
{
delete m_commandsAdapter;
}
void KisLayerManager::setView(QPointer<KisView>view)
{
m_imageView = view;
}
KisLayerSP KisLayerManager::activeLayer()
{
if (m_imageView) {
return m_imageView->currentLayer();
}
return 0;
}
KisPaintDeviceSP KisLayerManager::activeDevice()
{
if (activeLayer()) {
return activeLayer()->paintDevice();
}
return 0;
}
void KisLayerManager::activateLayer(KisLayerSP layer)
{
if (m_imageView) {
emit sigLayerActivated(layer);
layersUpdated();
if (layer) {
m_view->resourceProvider()->slotNodeActivated(layer.data());
}
}
}
void KisLayerManager::setup(KisActionManager* actionManager)
{
m_imageFlatten = new KisAction(i18n("&Flatten image"), this);
m_imageFlatten->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("flatten_image", m_imageFlatten);
m_imageFlatten->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_E));
connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage()));
m_imageMergeLayer = new KisAction(i18n("&Merge with Layer Below"), this);
m_imageMergeLayer->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("merge_layer", m_imageMergeLayer);
m_imageMergeLayer->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer()));
m_flattenLayer = new KisAction(i18n("&Flatten Layer"), this);
m_flattenLayer->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("flatten_layer", m_flattenLayer);
connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer()));
KisAction * action = new KisAction(i18n("Rename current layer"), this);
action->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("RenameCurrentLayer", action);
action->setShortcut(KShortcut(Qt::Key_F2));
connect(action, SIGNAL(triggered()), this, SLOT(layerProperties()));
m_rasterizeLayer = new KisAction(i18n("Rasterize Layer"), this);
m_rasterizeLayer->setActivationFlags(KisAction::ACTIVE_SHAPE_LAYER);
m_rasterizeLayer->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
actionManager->addAction("rasterize_layer", m_rasterizeLayer);
connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer()));
m_groupLayersSave = new KisAction(themedIcon("document-save"), i18n("Save Group Layers..."), this);
m_groupLayersSave->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("save_groups_as_images", m_groupLayersSave);
connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers()));
m_imageResizeToLayer = new KisAction(i18n("Trim to Current Layer"), this);
m_imageResizeToLayer->setActivationFlags(KisAction::ACTIVE_LAYER);
actionManager->addAction("resizeimagetolayer", m_imageResizeToLayer);
connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer()));
KisAction *trimToImage = new KisAction(themedIcon("trim-to-image"), i18n("Trim to Image Size"), this);
trimToImage->setActivationFlags(KisAction::ACTIVE_IMAGE);
actionManager->addAction("trim_to_image", trimToImage);
connect(trimToImage, SIGNAL(triggered()), this, SLOT(trimToImage()));
m_layerStyle = new KisAction(i18n("Layer Style..."), this);
m_layerStyle->setActivationFlags(KisAction::ACTIVE_LAYER);
m_layerStyle->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
actionManager->addAction("layer_style", m_layerStyle);
connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle()));
}
void KisLayerManager::updateGUI()
{
KisImageWSP image = m_view->image();
KisLayerSP layer;
qint32 nlayers = 0;
if (image) {
layer = activeLayer();
nlayers = image->nlayers();
}
// XXX these should be named layer instead of image
m_imageFlatten->setEnabled(nlayers > 1);
m_imageMergeLayer->setEnabled(nlayers > 1 && layer && layer->prevSibling());
m_flattenLayer->setEnabled(nlayers > 1 && layer && layer->firstChild());
if (m_view->statusBar())
m_view->statusBar()->setProfile(image);
}
void KisLayerManager::imageResizeToActiveLayer()
{
KisLayerSP layer;
KisImageWSP image = m_view->image();
if (image && (layer = activeLayer())) {
QRect cropRect = layer->projection()->nonDefaultPixelArea();
if (!cropRect.isEmpty()) {
image->cropImage(cropRect);
} else {
m_view->showFloatingMessage(
i18nc("floating message in layer manager",
"Layer is empty "),
QIcon(), 2000, KisFloatingMessage::Low);
}
}
}
void KisLayerManager::trimToImage()
{
KisImageWSP image = m_view->image();
if (image) {
image->cropImage(image->bounds());
}
}
void KisLayerManager::layerProperties()
{
if (!m_view) return;
if (!m_view->document()) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
if (KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()))) {
KisPaintDeviceSP dev = alayer->projection();
KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops");
dlg.resize(dlg.minimumSizeHint());
KisSafeFilterConfigurationSP configBefore(alayer->filter());
KIS_ASSERT_RECOVER_RETURN(configBefore);
QString xmlBefore = configBefore->toXML();
if (dlg.exec() == QDialog::Accepted) {
alayer->setName(dlg.layerName());
KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
= new KisChangeFilterCmd(alayer,
configBefore->name(),
xmlBefore,
configAfter->name(),
xmlAfter,
false);
// FIXME: check whether is needed
cmd->redo();
m_view->undoAdapter()->addCommand(cmd);
m_view->document()->setModified(true);
}
}
else {
KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
alayer->setDirty();
}
}
}
else if (KisGeneratorLayerSP alayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()))) {
KisDlgGeneratorLayer dlg(alayer->name(), m_view, m_view->mainWindow());
dlg.setCaption(i18n("Fill Layer Properties"));
KisSafeFilterConfigurationSP configBefore(alayer->filter());
Q_ASSERT(configBefore);
QString xmlBefore = configBefore->toXML();
dlg.setConfiguration(configBefore.data());
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
alayer->setName(dlg.layerName());
KisSafeFilterConfigurationSP configAfter(dlg.configuration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
= new KisChangeFilterCmd(alayer,
configBefore->name(),
xmlBefore,
configAfter->name(),
xmlAfter,
true);
// FIXME: check whether is needed
cmd->redo();
m_view->undoAdapter()->addCommand(cmd);
m_view->document()->setModified(true);
}
}
} else { // If layer == normal painting layer, vector layer, or group layer
KisDlgLayerProperties *dialog = new KisDlgLayerProperties(layer, m_view, m_view->document());
dialog->resize(dialog->minimumSizeHint());
dialog->setAttribute(Qt::WA_DeleteOnClose);
Qt::WindowFlags flags = dialog->windowFlags();
dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog);
dialog->show();
}
}
void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source)
{
KisImageWSP image = m_view->image();
if (!image) return;
KisPaintDeviceSP srcDevice =
source->paintDevice() ? source->projection() : source->original();
if (!srcDevice) return;
KisPaintDeviceSP clone;
if (!(*srcDevice->colorSpace() ==
*srcDevice->compositionSourceColorSpace())) {
clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace());
QRect rc(srcDevice->extent());
KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc);
} else {
clone = new KisPaintDevice(*srcDevice);
}
KisLayerSP layer = new KisPaintLayer(image,
source->name(),
source->opacity(),
clone);
layer->setCompositeOp(source->compositeOpId());
KisNodeSP parent = source->parent();
KisNodeSP above = source;
while (parent && !parent->allowAsChild(layer)) {
above = above->parent();
parent = above ? above->parent() : 0;
}
m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer"));
m_commandsAdapter->addNode(layer, parent, above);
m_commandsAdapter->removeNode(source);
m_commandsAdapter->endMacro();
}
void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above)
{
Q_ASSERT(activeNode);
parent = activeNode;
above = parent->lastChild();
while (parent &&
(!parent->allowAsChild(node) || parent->userLocked())) {
above = parent;
parent = parent->parent();
}
if (!parent) {
- qWarning() << "KisLayerManager::adjustLayerPosition:"
+ warnKrita << "KisLayerManager::adjustLayerPosition:"
<< "No node accepted newly created node";
parent = m_view->image()->root();
above = parent->lastChild();
}
}
void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer)
{
KisNodeSP parent;
KisNodeSP above;
adjustLayerPosition(layer, activeNode, parent, above);
m_commandsAdapter->addNode(layer, parent, above);
}
void KisLayerManager::addLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
addLayerCommon(activeNode,
new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()));
}
void KisLayerManager::addGroupLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
addLayerCommon(activeNode,
new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8));
}
void KisLayerManager::addCloneLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
addLayerCommon(activeNode,
new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8));
}
void KisLayerManager::addShapeLayer(KisNodeSP activeNode)
{
if (!m_view) return;
if (!m_view->document()) return;
KisImageWSP image = m_view->image();
KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, layer);
}
void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisSelectionSP selection = m_view->selection();
KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection);
image->refreshGraph();
KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original());
KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view);
dlg.resize(dlg.minimumSizeHint());
// ensure that the device may be free'd by the dialog
// when it is not needed anymore
previewDevice = 0;
if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) {
// XXX: add messagebox warning if there's no filter set!
m_commandsAdapter->undoLastCommand();
} else {
adjl->setName(dlg.layerName());
}
}
KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name,
KisFilterConfiguration * filter, KisSelectionSP selection)
{
KisImageWSP image = m_view->image();
KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection);
addLayerCommon(activeNode, layer);
return layer;
}
void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow());
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
KisSelectionSP selection = m_view->selection();
KisFilterConfiguration * generator = dlg.configuration();
QString name = dlg.layerName();
addLayerCommon(activeNode,
new KisGeneratorLayer(image, name, generator, selection));
}
}
void KisLayerManager::layerDuplicate()
{
KisImageWSP image = m_view->image();
if (!image)
return;
KisLayerSP active = activeLayer();
if (!active)
return;
KisLayerSP dup = dynamic_cast<KisLayer*>(active->clone().data());
m_commandsAdapter->addNode(dup.data(), active->parent(), active.data());
if (dup) {
activateLayer(dup);
} else {
QMessageBox::critical(m_view->mainWindow(),
i18nc("@title:window", "Krita"),
i18n("Could not add layer to image."));
}
}
void KisLayerManager::layerRaise()
{
KisImageWSP image = m_view->image();
KisLayerSP layer;
if (!image)
return;
layer = activeLayer();
m_commandsAdapter->raise(layer);
layer->parent()->setDirty();
}
void KisLayerManager::layerLower()
{
KisImageWSP image = m_view->image();
KisLayerSP layer;
if (!image)
return;
layer = activeLayer();
m_commandsAdapter->lower(layer);
layer->parent()->setDirty();
}
void KisLayerManager::layerFront()
{
KisImageWSP image = m_view->image();
KisLayerSP layer;
if (!image)
return;
layer = activeLayer();
m_commandsAdapter->toTop(layer);
layer->parent()->setDirty();
}
void KisLayerManager::layerBack()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer;
layer = activeLayer();
m_commandsAdapter->toBottom(layer);
layer->parent()->setDirty();
}
void KisLayerManager::rotateLayer(double radians)
{
if (!m_view->image()) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
m_view->image()->rotateNode(layer, radians);
}
void KisLayerManager::shearLayer(double angleX, double angleY)
{
if (!m_view->image()) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
m_view->image()->shearNode(layer, angleX, angleY);
}
void KisLayerManager::flattenImage()
{
KisImageWSP image = m_view->image();
if (image) {
bool doIt = true;
if (image->nHiddenLayers() > 0) {
int answer = QMessageBox::warning(m_view->mainWindow(),
i18nc("@title:window", "Flatten Image"),
i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer != QMessageBox::Yes) {
doIt = false;
}
}
if (doIt) {
image->flatten();
}
}
}
inline bool isSelectionMask(KisNodeSP node) {
return dynamic_cast<KisSelectionMask*>(node.data());
}
bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image)
{
bool result = false;
KisNodeSP prevNode = currentNode->prevSibling();
if (isSelectionMask(currentNode) &&
prevNode && isSelectionMask(prevNode)) {
QList<KisNodeSP> mergedNodes;
mergedNodes.append(currentNode);
mergedNodes.append(prevNode);
image->mergeMultipleLayers(mergedNodes, currentNode);
result = true;
}
return result;
}
void KisLayerManager::mergeLayer()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
if (selectedNodes.size() > 1) {
image->mergeMultipleLayers(selectedNodes, layer);
} else if (!tryMergeSelectionMasks(m_view->activeNode(), image)) {
if (!layer->prevSibling()) return;
KisLayer *prevLayer = dynamic_cast<KisLayer*>(layer->prevSibling().data());
if (!prevLayer) return;
if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) {
image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
}
else {
const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow());
if (!strategy) return;
image->mergeDown(layer, strategy);
}
}
m_view->updateGUI();
}
void KisLayerManager::flattenLayer()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
KisLayerSP newLayer = image->flattenLayer(layer);
if (newLayer) {
newLayer->setDirty();
}
m_view->updateGUI();
}
void KisLayerManager::rasterizeLayer()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity());
KisPainter gc(paintLayer->paintDevice());
QRect rc = layer->projection()->exactBounds();
gc.bitBlt(rc.topLeft(), layer->projection(), rc);
m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer"));
m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data());
int childCount = layer->childCount();
for (int i = 0; i < childCount; i++) {
m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild());
}
m_commandsAdapter->removeNode(layer);
m_commandsAdapter->endMacro();
updateGUI();
}
void KisLayerManager::layersUpdated()
{
KisLayerSP layer = activeLayer();
if (!layer) return;
m_view->updateGUI();
}
void KisLayerManager::saveGroupLayers()
{
QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export);
KDialog dlg;
QWidget *page = new QWidget(&dlg);
dlg.setMainWidget(page);
QBoxLayout *layout = new QVBoxLayout(page);
KFileWidget *fd = new KFileWidget(m_view->document()->url().path(), page);
fd->setUrl(m_view->document()->url());
fd->setMimeFilter(listMimeFilter);
fd->setOperationMode(KFileWidget::Saving);
layout->addWidget(fd);
QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page);
chkInvisible->setChecked(false);
layout->addWidget(chkInvisible);
QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page);
chkDepth->setChecked(true);
layout->addWidget(chkDepth);
if (!dlg.exec()) return;
// selectedUrl()( does not return the expected result. So, build up the KUrl the more complicated way
//return m_fileWidget->selectedUrl();
KUrl url = fd->dirOperator()->url();
url.adjustPath(KUrl::AddTrailingSlash);
QString path = fd->locationEdit()->currentText();
QFileInfo f(path);
QString extension = f.completeSuffix();
QString basename = f.baseName();
QString mimefilter = fd->currentMimeFilter();
if (mimefilter.isEmpty()) {
KMimeType::Ptr mime = KMimeType::findByUrl(url);
mimefilter = mime->name();
extension = mime->extractKnownExtension(path);
}
if (url.isEmpty())
return;
KisImageWSP image = m_view->image();
if (!image) return;
KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), url, basename, extension, mimefilter);
image->rootLayer()->accept(v);
}
bool KisLayerManager::activeLayerHasSelection()
{
return (activeLayer()->selection() != 0);
}
void KisLayerManager::addFileLayer(KisNodeSP activeNode)
{
QString basePath;
KUrl url = m_view->document()->url();
if (url.isLocalFile()) {
basePath = QFileInfo(url.toLocalFile()).absolutePath();
}
KisImageWSP image = m_view->image();
KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow());
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
QString name = dlg.layerName();
QString fileName = dlg.fileName();
if(fileName.isEmpty()){
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return;
}
KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution();
addLayerCommon(activeNode,
new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8));
}
}
void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg)
{
KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone());
}
void KisLayerManager::layerStyle()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
KisPSDLayerStyleSP oldStyle;
if (layer->layerStyle()) {
oldStyle = layer->layerStyle()->clone();
}
else {
oldStyle = toQShared(new KisPSDLayerStyle());
}
KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider());
boost::function<void ()> updateCall(boost::bind(updateLayerStyles, layer, &dlg));
SignalToFunctionProxy proxy(updateCall);
connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start()));
if (dlg.exec() == QDialog::Accepted) {
KisPSDLayerStyleSP newStyle = dlg.style();
KUndo2CommandSP command = toQShared(
new KisSetLayerStyleCommand(layer, oldStyle, newStyle));
image->postExecutionUndoAdapter()->addCommand(command);
}
}
diff --git a/krita/ui/kis_md5_generator.cpp b/krita/ui/kis_md5_generator.cpp
index 8ce544b6e11..1a19933a525 100644
--- a/krita/ui/kis_md5_generator.cpp
+++ b/krita/ui/kis_md5_generator.cpp
@@ -1,64 +1,64 @@
/*
* Copyright (c) 2015 Stefano Bonicatti <smjert@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_md5_generator.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <KoStore.h>
KisMD5Generator::KisMD5Generator()
{
}
KisMD5Generator::~KisMD5Generator()
{
}
QByteArray KisMD5Generator::generateHash(QString filename)
{
QByteArray ba;
if(filename.startsWith("bundle://")) {
QString bn = filename.mid(9);
int pos = bn.lastIndexOf(":");
QString fn = bn.right(bn.size() - pos - 1);
bn = bn.left(pos);
QScopedPointer<KoStore> resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
if (!resourceStore || resourceStore->bad()) {
- qWarning() << "Could not open store on bundle" << bn;
+ warnKrita << "Could not open store on bundle" << bn;
return ba;
}
if (resourceStore->isOpen()) resourceStore->close();
if (!resourceStore->open(fn)) {
- qWarning() << "Could not open preset" << fn << "in bundle" << bn;
+ warnKrita << "Could not open preset" << fn << "in bundle" << bn;
return ba;
}
ba = resourceStore->device()->readAll();
resourceStore->close();
return KoMD5Generator::generateHash(ba);
}
return KoMD5Generator::generateHash(filename);
}
diff --git a/krita/ui/kis_min_heap.h b/krita/ui/kis_min_heap.h
index 2a41d9499e8..4f4775749ee 100644
--- a/krita/ui/kis_min_heap.h
+++ b/krita/ui/kis_min_heap.h
@@ -1,165 +1,165 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KIS_MIN_HEAP_H
#define KIS_MIN_HEAP_H
-#include <QDebug>
+#include <kis_debug.h>
#include <QList>
template <typename T> struct PriorityNode {
T data;
int key;
int pos;
};
template <typename T, int N> class KisMinHeap
{
public:
KisMinHeap(): m_list(0) {
m_size = N;
m_last = 0;
m_list = new PriorityNode <T>* [N];
}
inline KisMinHeap(const T& data, int key) {
KisMinHeap();
append(data, key);
}
inline KisMinHeap(PriorityNode<T>* node) {
KisMinHeap();
append(node);
}
~KisMinHeap() {
delete[] m_list;
}
inline void changeKey(int pos, int newKey) {
m_list[pos]->key = newKey;
heapifyUp(pos);
heapifyDown(pos);
}
inline int size() {
return m_last;
}
inline T valueAt(int pos) {
return m_list[pos]->data;
}
void append(PriorityNode<T>* node) {
node->pos = m_last;
m_list[m_last] = node;
++m_last;
heapifyUp(node->pos);
node = 0;
}
void append(const T& data, int key) {
if (m_last >= m_size) return;
PriorityNode <T>* node = new PriorityNode<T>;
node->data = data;
node->key = key;
append(node);
}
void remove(int pos) {
if (pos < 0) return;
swap(pos, m_last - 1);
--m_last;
delete m_list[m_last];
m_list[m_last] = 0;
heapifyUp(pos);
heapifyDown(pos);
}
void remove(const T& data) {
int pos = find(data);
if (pos >= 0) remove(pos);
}
int find(const T& data) {
for (int pos = 0; pos < m_last; pos++) {
if (m_list[pos]->data == data) return pos;
}
return -1;
}
private:
int m_last;
int m_size;
PriorityNode <T>* *m_list;
void swap(int pos1, int pos2) {
PriorityNode <T>* temp(m_list[pos1]);
m_list[pos1] = m_list[pos2];
m_list[pos1]->pos = pos1;
m_list[pos2] = temp;
m_list[pos2]->pos = pos2;
temp = 0;
}
void heapifyUp(int pos) {
while (pos > 0 && m_list[pos]->key < m_list[parentPos(pos)]->key) {
swap(pos, parentPos(pos));
pos = parentPos(pos);
}
}
void heapifyDown(int pos) {
if (leftChildPos(pos) >= m_last) return; //no children
else {
int childPos = 0;
if (rightChildPos(pos) >= m_last) { //1 child
childPos = leftChildPos(pos);
} else { //2 children
m_list[leftChildPos(pos)]->key < m_list[rightChildPos(pos)]->key ? childPos = leftChildPos(pos) :
childPos = rightChildPos(pos);
}
if (m_list[childPos]->key < m_list[pos]->key) {
swap(pos, childPos);
heapifyDown(childPos);
} else return;
}
}
inline int leftChildPos(int x) {
return 2 * x + 1;
}
inline int rightChildPos(int x) {
return 2 * x + 2;
}
inline int parentPos(int x) {
return (x - 1) / 2;
}
};
#endif // HEAP_H
diff --git a/krita/ui/kis_node_manager.cpp b/krita/ui/kis_node_manager.cpp
index 7c0d1ecfa77..6e8fef8f1d3 100644
--- a/krita/ui/kis_node_manager.cpp
+++ b/krita/ui/kis_node_manager.cpp
@@ -1,1150 +1,1150 @@
/*
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_node_manager.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <kactioncollection.h>
#include <kmimetype.h>
#include <kshortcut.h>
#include <KoIcon.h>
#include <KoSelection.h>
#include <KoShapeManager.h>
#include <KoShape.h>
#include <KoShapeLayer.h>
#include <KisImportExportManager.h>
#include <KoFileDialog.h>
#include <KoToolManager.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kis_types.h>
#include <kis_node.h>
#include <kis_selection.h>
#include <kis_selection_mask.h>
#include <kis_layer.h>
#include <kis_mask.h>
#include <kis_image.h>
#include <kis_painter.h>
#include <kis_paint_layer.h>
#include "KisPart.h"
#include "canvas/kis_canvas2.h"
#include "kis_shape_controller.h"
#include "kis_canvas_resource_provider.h"
#include "KisViewManager.h"
#include "KisDocument.h"
#include "kis_mask_manager.h"
#include "kis_group_layer.h"
#include "kis_layer_manager.h"
#include "kis_selection_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_processing_applicator.h"
#include "kis_sequential_iterator.h"
#include "kis_transaction.h"
#include "processing/kis_mirror_processing_visitor.h"
#include "KisView.h"
struct KisNodeManager::Private {
Private(KisNodeManager *_q)
: q(_q)
, view(0)
, imageView(0)
, layerManager(0)
, maskManager(0)
, self(0)
, commandsAdapter(0)
{
}
~Private() {
delete layerManager;
delete maskManager;
}
KisNodeManager *q;
KisViewManager * view;
QPointer<KisView>imageView;
KisLayerManager * layerManager;
KisMaskManager * maskManager;
KisNodeManager* self;
KisNodeCommandsAdapter* commandsAdapter;
QList<KisNodeSP> selectedNodes;
bool activateNodeImpl(KisNodeSP node);
QSignalMapper nodeCreationSignalMapper;
QSignalMapper nodeConversionSignalMapper;
void saveDeviceAsImage(KisPaintDeviceSP device,
const QString &defaultName,
const QRect &bounds,
qreal xRes,
qreal yRes,
quint8 opacity);
void mergeTransparencyMaskAsAlpha(bool writeToLayers);
};
bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node)
{
Q_ASSERT(view);
Q_ASSERT(view->canvasBase());
Q_ASSERT(view->canvasBase()->globalShapeManager());
Q_ASSERT(imageView);
if (node && node == self->activeNode()) {
return false;
}
// Set the selection on the shape manager to the active layer
// and set call KoSelection::setActiveLayer( KoShapeLayer* layer )
// with the parent of the active layer.
KoSelection *selection = view->canvasBase()->globalShapeManager()->selection();
Q_ASSERT(selection);
selection->deselectAll();
if (!node) {
selection->setActiveLayer(0);
imageView->setCurrentNode(0);
maskManager->activateMask(0);
layerManager->activateLayer(0);
} else {
KoShape * shape = view->document()->shapeForNode(node);
Q_ASSERT(shape);
selection->select(shape);
KoShapeLayer * shapeLayer = dynamic_cast<KoShapeLayer*>(shape);
Q_ASSERT(shapeLayer);
// shapeLayer->setGeometryProtected(node->userLocked());
// shapeLayer->setVisible(node->visible());
selection->setActiveLayer(shapeLayer);
imageView->setCurrentNode(node);
if (KisLayerSP layer = dynamic_cast<KisLayer*>(node.data())) {
maskManager->activateMask(0);
layerManager->activateLayer(layer);
} else if (KisMaskSP mask = dynamic_cast<KisMask*>(node.data())) {
maskManager->activateMask(mask);
// XXX_NODE: for now, masks cannot be nested.
layerManager->activateLayer(static_cast<KisLayer*>(node->parent().data()));
}
}
return true;
}
KisNodeManager::KisNodeManager(KisViewManager *view)
: m_d(new Private(this))
{
m_d->view = view;
m_d->layerManager = new KisLayerManager(view);
m_d->maskManager = new KisMaskManager(view);
m_d->self = this;
m_d->commandsAdapter = new KisNodeCommandsAdapter(view);
connect(m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP)));
}
KisNodeManager::~KisNodeManager()
{
delete m_d->commandsAdapter;
delete m_d;
}
void KisNodeManager::setView(QPointer<KisView>imageView)
{
m_d->maskManager->setView(imageView);
m_d->layerManager->setView(imageView);
if (m_d->imageView) {
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this);
m_d->imageView->image()->disconnect(this);
}
m_d->imageView = imageView;
if (m_d->imageView) {
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP)));
connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()),this, SLOT(slotUpdateIsolateModeAction()));
m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode());
}
}
#define NEW_LAYER_ACTION(id, text, layerType, icon) \
{ \
action = new KisAction(icon, text, this); \
action->setActivationFlags(KisAction::ACTIVE_NODE); \
actionManager->addAction(id, action); \
m_d->nodeCreationSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeCreationSignalMapper, SLOT(map())); \
}
#define NEW_LAYER_ACTION_KEY(id, text, layerType, icon, shortcut) \
{ \
NEW_LAYER_ACTION(id, text, layerType, icon); \
action->setShortcut(KShortcut(shortcut)); \
}
#define NEW_MASK_ACTION(id, text, layerType, icon) \
{ \
NEW_LAYER_ACTION(id, text, layerType, icon); \
action->setActivationFlags(KisAction::ACTIVE_LAYER); \
}
#define CONVERT_NODE_ACTION(id, text, layerType, icon) \
{ \
action = new KisAction(icon, text, this); \
action->setActivationFlags(KisAction::ACTIVE_NODE); \
action->setExcludedNodeTypes(QStringList(layerType)); \
actionManager->addAction(id, action); \
m_d->nodeConversionSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeConversionSignalMapper, SLOT(map())); \
}
void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager)
{
m_d->layerManager->setup(actionManager);
m_d->maskManager->setup(actionCollection, actionManager);
KisAction * action = new KisAction(themedIcon("symmetry-horizontal"), i18n("Mirror Layer Horizontally"), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
actionManager->addAction("mirrorNodeX", action);
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
action = new KisAction(themedIcon("symmetry-vertical"), i18n("Mirror Layer Vertically"), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
actionManager->addAction("mirrorNodeY", action);
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
action = new KisAction(i18n("Activate next layer"), this);
action->setActivationFlags(KisAction::ACTIVE_LAYER);
action->setShortcut(KShortcut(Qt::Key_PageUp));
actionManager->addAction("activateNextLayer", action);
connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
action = new KisAction(i18n("Activate previous layer"), this);
action->setActivationFlags(KisAction::ACTIVE_LAYER);
action->setShortcut(KShortcut(Qt::Key_PageDown));
actionManager->addAction("activatePreviousLayer", action);
connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode()));
action = new KisAction(themedIcon("document-save"), i18n("Save Layer/Mask..."), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
actionManager->addAction("save_node_as_image", action);
connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage()));
action = new KisAction(themedIcon("edit-copy"), i18n("&Duplicate Layer or Mask"), this);
action->setActivationFlags(KisAction::ACTIVE_NODE);
action->setShortcut(KShortcut(Qt::ControlModifier + Qt::Key_J));
actionManager->addAction("duplicatelayer", action);
connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode()));
NEW_LAYER_ACTION_KEY("add_new_paint_layer", i18n("&Paint Layer"),
"KisPaintLayer", themedIcon("document-new"),
Qt::Key_Insert);
NEW_LAYER_ACTION_KEY("add_new_group_layer", i18n("&Group Layer"),
"KisGroupLayer", themedIcon("folder"),
Qt::ControlModifier + Qt::Key_G);
NEW_LAYER_ACTION("add_new_clone_layer", i18n("&Clone Layer"),
"KisCloneLayer", themedIcon("edit-copy"));
NEW_LAYER_ACTION("add_new_shape_layer", i18n("&Vector Layer"),
"KisShapeLayer", themedIcon("bookmarks"));
NEW_LAYER_ACTION("add_new_adjustment_layer", i18n("&Filter Layer..."),
"KisAdjustmentLayer", themedIcon("view-filter"));
NEW_LAYER_ACTION("add_new_fill_layer", i18n("&Fill Layer..."),
"KisGeneratorLayer", themedIcon("krita_tool_color_fill"));
NEW_LAYER_ACTION("add_new_file_layer", i18n("&File Layer..."),
"KisFileLayer", themedIcon("document-open"));
NEW_MASK_ACTION("add_new_transparency_mask", i18n("&Transparency Mask"),
"KisTransparencyMask", themedIcon("edit-copy"));
NEW_MASK_ACTION("add_new_filter_mask", i18n("&Filter Mask..."),
"KisFilterMask", themedIcon("view-filter"));
NEW_MASK_ACTION("add_new_transform_mask", i18n("&Transform Mask..."),
"KisTransformMask", themedIcon("bookmarks"));
NEW_MASK_ACTION("add_new_selection_mask", i18n("&Local Selection"),
"KisSelectionMask", themedIcon("edit-paste"));
connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(const QString &)),
this, SLOT(createNode(const QString &)));
CONVERT_NODE_ACTION("convert_to_paint_layer", i18n("to &Paint Layer"),
"KisPaintLayer", themedIcon("document-new"));
CONVERT_NODE_ACTION("convert_to_selection_mask", i18n("to &Selection Mask"),
"KisSelectionMask", themedIcon("edit-paste"));
CONVERT_NODE_ACTION("convert_to_filter_mask", i18n("to &Filter Mask..."),
"KisFilterMask", themedIcon("view-filter"));
CONVERT_NODE_ACTION("convert_to_transparency_mask", i18n("to &Transparency Mask"),
"KisTransparencyMask", themedIcon("edit-copy"));
connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)),
this, SLOT(convertNode(const QString &)));
action = new KisAction(themedIcon("layer-visible-off"), i18n("&Isolate Layer"), this);
action->setCheckable(true);
action->setActivationFlags(KisAction::ACTIVE_NODE);
actionManager->addAction("isolate_layer", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool)));
action = new KisAction(themedIcon("edit-copy"), i18n("Alpha into Mask"), this);
action->setActivationFlags(KisAction::ACTIVE_LAYER);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE);
actionManager->addAction("split_alpha_into_mask", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
action = new KisAction(themedIcon("transparency-enabled"), i18n("Write as Alpha"), this);
action->setActivationFlags(KisAction::ACTIVE_TRANSPARENCY_MASK);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
actionManager->addAction("split_alpha_write", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
action = new KisAction(themedIcon("document-save"), i18n("Save Merged..."), this);
action->setActivationFlags(KisAction::ACTIVE_TRANSPARENCY_MASK);
// HINT: we can save even when the nodes are not editable
actionManager->addAction("split_alpha_save_merged", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged()));
connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction()));
connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryFinishIsolatedMode()));
}
void KisNodeManager::updateGUI()
{
// enable/disable all relevant actions
m_d->layerManager->updateGUI();
m_d->maskManager->updateGUI();
}
KisNodeSP KisNodeManager::activeNode()
{
if (m_d->imageView) {
return m_d->imageView->currentNode();
}
return 0;
}
KisLayerSP KisNodeManager::activeLayer()
{
return m_d->layerManager->activeLayer();
}
const KoColorSpace* KisNodeManager::activeColorSpace()
{
Q_ASSERT(m_d->maskManager);
if (m_d->maskManager->activeDevice()) {
// Q_ASSERT(m_d->maskManager->activeDevice());
return m_d->maskManager->activeDevice()->colorSpace();
} else {
Q_ASSERT(m_d->layerManager);
Q_ASSERT(m_d->layerManager->activeLayer());
if (m_d->layerManager->activeLayer()->parentLayer())
return m_d->layerManager->activeLayer()->parentLayer()->colorSpace();
else
return m_d->view->image()->colorSpace();
}
}
void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
{
if (parent->allowAsChild(node)) {
if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) {
KisSelectionMask *m = dynamic_cast<KisSelectionMask*>(node.data());
KisLayer *l = dynamic_cast<KisLayer*>(parent.data());
KisSelectionMaskSP selMask = l->selectionMask();
if (m && m->active() && l && l->selectionMask())
selMask->setActive(false);
}
m_d->commandsAdapter->moveNode(node, parent, index);
}
}
void KisNodeManager::addNodeDirect(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
{
Q_ASSERT(parent->allowAsChild(node));
m_d->commandsAdapter->addNode(node, parent, aboveThis);
}
void KisNodeManager::moveNodeDirect(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis)
{
Q_ASSERT(parent->allowAsChild(node));
m_d->commandsAdapter->moveNode(node, parent, aboveThis);
}
void KisNodeManager::toggleIsolateActiveNode()
{
KisImageWSP image = m_d->view->image();
KisNodeSP activeNode = this->activeNode();
KIS_ASSERT_RECOVER_RETURN(activeNode);
if (activeNode == image->isolatedModeRoot()) {
toggleIsolateMode(false);
} else {
toggleIsolateMode(true);
}
}
void KisNodeManager::toggleIsolateMode(bool checked)
{
KisImageWSP image = m_d->view->image();
if (checked) {
KisNodeSP activeNode = this->activeNode();
// Transform masks don't have pixel data...
if (activeNode->inherits("KisTransformMask")) return;
KIS_ASSERT_RECOVER_RETURN(activeNode);
if (!image->startIsolatedMode(activeNode)) {
KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer");
action->setChecked(false);
}
} else {
image->stopIsolatedMode();
}
}
void KisNodeManager::slotUpdateIsolateModeAction()
{
KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer");
Q_ASSERT(action);
KisNodeSP activeNode = this->activeNode();
KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
action->setChecked(isolatedRootNode && isolatedRootNode == activeNode);
}
void KisNodeManager::slotTryFinishIsolatedMode()
{
KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
if (!isolatedRootNode) return;
bool belongsToIsolatedGroup = false;
KisNodeSP node = this->activeNode();
while(node) {
if (node == isolatedRootNode) {
belongsToIsolatedGroup = true;
break;
}
node = node->parent();
}
if (!belongsToIsolatedGroup) {
m_d->view->image()->stopIsolatedMode();
}
}
void KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom)
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) {
activeNode = m_d->view->image()->root();
}
KIS_ASSERT_RECOVER_RETURN(activeNode);
if (activeNode->systemLocked()) {
return;
}
// XXX: make factories for this kind of stuff,
// with a registry
if (nodeType == "KisPaintLayer") {
m_d->layerManager->addLayer(activeNode);
} else if (nodeType == "KisGroupLayer") {
m_d->layerManager->addGroupLayer(activeNode);
} else if (nodeType == "KisAdjustmentLayer") {
m_d->layerManager->addAdjustmentLayer(activeNode);
} else if (nodeType == "KisGeneratorLayer") {
m_d->layerManager->addGeneratorLayer(activeNode);
} else if (nodeType == "KisShapeLayer") {
m_d->layerManager->addShapeLayer(activeNode);
} else if (nodeType == "KisCloneLayer") {
m_d->layerManager->addCloneLayer(activeNode);
} else if (nodeType == "KisTransparencyMask") {
m_d->maskManager->createTransparencyMask(activeNode, copyFrom, false);
} else if (nodeType == "KisFilterMask") {
m_d->maskManager->createFilterMask(activeNode, copyFrom, quiet, false);
} else if (nodeType == "KisTransformMask") {
m_d->maskManager->createTransformMask(activeNode);
} else if (nodeType == "KisSelectionMask") {
m_d->maskManager->createSelectionMask(activeNode, copyFrom, false);
} else if (nodeType == "KisFileLayer") {
m_d->layerManager->addFileLayer(activeNode);
}
}
void KisNodeManager::convertNode(const QString &nodeType)
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
if (nodeType == "KisPaintLayer") {
m_d->layerManager->convertNodeToPaintLayer(activeNode);
} else if (nodeType == "KisSelectionMask" ||
nodeType == "KisFilterMask" ||
nodeType == "KisTransparencyMask") {
KisPaintDeviceSP copyFrom = activeNode->paintDevice() ?
activeNode->paintDevice() : activeNode->projection();
m_d->commandsAdapter->beginMacro(kundo2_i18n("Convert to a Selection Mask"));
if (nodeType == "KisSelectionMask") {
m_d->maskManager->createSelectionMask(activeNode, copyFrom, true);
} else if (nodeType == "KisFilterMask") {
m_d->maskManager->createFilterMask(activeNode, copyFrom, false, true);
} else if (nodeType == "KisTransparencyMask") {
m_d->maskManager->createTransparencyMask(activeNode, copyFrom, true);
}
m_d->commandsAdapter->removeNode(activeNode);
m_d->commandsAdapter->endMacro();
} else {
- qWarning() << "Unsupported node conversion type:" << nodeType;
+ warnKrita << "Unsupported node conversion type:" << nodeType;
}
}
void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
{
if (m_d->activateNodeImpl(node)) {
emit sigUiNeedChangeActiveNode(node);
emit sigNodeActivated(node);
nodesUpdated();
if (node) {
bool toggled = m_d->view->actionCollection()->action("view_show_just_the_canvas")->isChecked();
if (toggled) {
m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
}
}
}
}
void KisNodeManager::slotUiActivatedNode(KisNodeSP node)
{
if (m_d->activateNodeImpl(node)) {
emit sigNodeActivated(node);
nodesUpdated();
}
if (node) {
QStringList vectorTools = QStringList()
<< "InteractionTool"
<< "KarbonPatternTool"
<< "KarbonGradientTool"
<< "KarbonCalligraphyTool"
<< "CreateShapesTool"
<< "PathToolFactoryId";
QStringList pixelTools = QStringList()
<< "KritaShape/KisToolBrush"
<< "KritaShape/KisToolDyna"
<< "KritaShape/KisToolMultiBrush"
<< "KritaFill/KisToolFill"
<< "KritaFill/KisToolGradient";
if (node->inherits("KisShapeLayer")) {
if (pixelTools.contains(KoToolManager::instance()->activeToolId())) {
KoToolManager::instance()->switchToolRequested("InteractionTool");
}
}
else {
if (vectorTools.contains(KoToolManager::instance()->activeToolId())) {
KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
}
}
}
}
void KisNodeManager::nodesUpdated()
{
KisNodeSP node = activeNode();
if (!node) return;
m_d->layerManager->layersUpdated();
m_d->maskManager->masksUpdated();
m_d->view->updateGUI();
m_d->view->selectionManager()->selectionChanged();
}
KisPaintDeviceSP KisNodeManager::activePaintDevice()
{
return m_d->maskManager->activeMask() ?
m_d->maskManager->activeDevice() :
m_d->layerManager->activeDevice();
}
void KisNodeManager::nodeProperties(KisNodeSP node)
{
if (node->inherits("KisLayer")) {
m_d->layerManager->layerProperties();
} else if (node->inherits("KisMask")) {
m_d->maskManager->maskProperties();
}
}
qint32 KisNodeManager::convertOpacityToInt(qreal opacity)
{
/**
* Scales opacity from the range 0...100
* to the integer range 0...255
*/
return qMin(255, int(opacity * 2.55 + 0.5));
}
void KisNodeManager::setNodeOpacity(KisNodeSP node, qint32 opacity,
bool finalChange)
{
if (!node) return;
if (node->opacity() == opacity) return;
if (!finalChange) {
node->setOpacity(opacity);
node->setDirty();
} else {
m_d->commandsAdapter->setOpacity(node, opacity);
}
}
void KisNodeManager::setNodeCompositeOp(KisNodeSP node,
const KoCompositeOp* compositeOp)
{
if (!node) return;
if (node->compositeOp() == compositeOp) return;
m_d->commandsAdapter->setCompositeOp(node, compositeOp);
}
void KisNodeManager::setSelectedNodes(QList<KisNodeSP> nodes)
{
m_d->selectedNodes = nodes;
}
QList<KisNodeSP> KisNodeManager::selectedNodes()
{
return m_d->selectedNodes;
}
void KisNodeManager::nodeOpacityChanged(qreal opacity, bool finalChange)
{
KisNodeSP node = activeNode();
setNodeOpacity(node, convertOpacityToInt(opacity), finalChange);
}
void KisNodeManager::nodeCompositeOpChanged(const KoCompositeOp* op)
{
KisNodeSP node = activeNode();
setNodeCompositeOp(node, op);
}
void KisNodeManager::duplicateActiveNode()
{
KisNodeSP node = activeNode();
// FIXME: can't imagine how it may happen
Q_ASSERT(node);
if (node->inherits("KisLayer")) {
m_d->layerManager->layerDuplicate();
} else if (node->inherits("KisMask")) {
m_d->maskManager->duplicateMask();
}
}
void KisNodeManager::raiseNode()
{
// The user sees the layer stack topsy-turvy, as a tree with the
// root at the bottom instead of on top.
KisNodeSP node = activeNode();
if (node->inherits("KisLayer")) {
m_d->layerManager->layerLower();
} else if (node->inherits("KisMask")) {
m_d->maskManager->lowerMask();
}
}
void KisNodeManager::lowerNode()
{
// The user sees the layer stack topsy-turvy, as a tree with the
// root at the bottom instead of on top.
KisNodeSP node = activeNode();
if (node->inherits("KisLayer")) {
m_d->layerManager->layerRaise();
} else if (node->inherits("KisMask")) {
m_d->maskManager->raiseMask();
}
}
void KisNodeManager::nodeToTop()
{
KisNodeSP node = activeNode();
if (node->inherits("KisLayer")) {
m_d->layerManager->layerBack();
} else if (node->inherits("KisMask")) {
m_d->maskManager->maskToBottom();
}
}
void KisNodeManager::nodeToBottom()
{
KisNodeSP node = activeNode();
if (node->inherits("KisLayer")) {
m_d->layerManager->layerLower();
} else if (node->inherits("KisMask")) {
m_d->maskManager->maskToTop();
}
}
bool scanForLastLayer(KisImageWSP image, KisNodeSP nodeToRemove)
{
if (!dynamic_cast<KisLayer*>(nodeToRemove.data())) {
return false;
}
bool lastLayer = true;
KisNodeSP node = image->root()->firstChild();
while (node) {
if (node != nodeToRemove && dynamic_cast<KisLayer*>(node.data())) {
lastLayer = false;
break;
}
node = node->nextSibling();
}
return lastLayer;
}
/// Scan whether the node has a parent in the list of nodes
bool scanForParent(QList<KisNodeSP> nodeList, KisNodeSP node)
{
KisNodeSP parent = node->parent();
while (parent) {
if (nodeList.contains(parent)) {
return true;
}
parent = parent->parent();
}
return false;
}
void KisNodeManager::removeSingleNode(KisNodeSP node)
{
if (!node || !node->parent()) {
return;
}
if (scanForLastLayer(m_d->view->image(), node)) {
m_d->commandsAdapter->beginMacro(kundo2_i18n("Remove Last Layer"));
m_d->commandsAdapter->removeNode(node);
// An oddity, but this is required as for some reason, we can end up in a situation
// where our active node is still set to one of the layers removed above.
activeNode().clear();
createNode("KisPaintLayer");
m_d->commandsAdapter->endMacro();
} else {
m_d->commandsAdapter->removeNode(node);
}
}
void KisNodeManager::removeSelectedNodes(QList<KisNodeSP> selectedNodes)
{
m_d->commandsAdapter->beginMacro(kundo2_i18n("Remove Multiple Layers and Masks"));
foreach(KisNodeSP node, selectedNodes) {
if (!scanForParent(selectedNodes, node)) {
removeSingleNode(node);
}
}
m_d->commandsAdapter->endMacro();
}
void KisNodeManager::removeNode()
{
//do not delete root layer
if (m_d->selectedNodes.count() > 1) {
removeSelectedNodes(m_d->selectedNodes);
}
else {
removeSingleNode(activeNode());
}
}
void KisNodeManager::mirrorNodeX()
{
KisNodeSP node = activeNode();
KUndo2MagicString commandName;
if (node->inherits("KisLayer")) {
commandName = kundo2_i18n("Mirror Layer X");
} else if (node->inherits("KisMask")) {
commandName = kundo2_i18n("Mirror Mask X");
}
mirrorNode(node, commandName, Qt::Horizontal);
}
void KisNodeManager::mirrorNodeY()
{
KisNodeSP node = activeNode();
KUndo2MagicString commandName;
if (node->inherits("KisLayer")) {
commandName = kundo2_i18n("Mirror Layer Y");
} else if (node->inherits("KisMask")) {
commandName = kundo2_i18n("Mirror Mask Y");
}
mirrorNode(node, commandName, Qt::Vertical);
}
inline bool checkForGlobalSelection(KisNodeSP node) {
return dynamic_cast<KisSelectionMask*>(node.data()) && node->parent() && !node->parent()->parent();
}
void KisNodeManager::activateNextNode()
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
KisNodeSP node = activeNode->nextSibling();
if (!node && activeNode->parent() && activeNode->parent()->parent()) {
node = activeNode->parent();
}
while(node && checkForGlobalSelection(node)) {
node = node->nextSibling();
}
if (node) {
slotNonUiActivatedNode(node);
}
}
void KisNodeManager::activatePreviousNode()
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
KisNodeSP node = activeNode->prevSibling();
if (!node && activeNode->parent()) {
node = activeNode->parent()->prevSibling();
}
while(node && checkForGlobalSelection(node)) {
node = node->prevSibling();
}
if (node) {
slotNonUiActivatedNode(node);
}
}
void KisNodeManager::mergeLayer()
{
m_d->layerManager->mergeLayer();
}
void KisNodeManager::rotate(double radians)
{
// XXX: implement rotation for masks as well
m_d->layerManager->rotateLayer(radians);
}
void KisNodeManager::rotate180()
{
rotate(M_PI);
}
void KisNodeManager::rotateLeft90()
{
rotate(-M_PI / 2);
}
void KisNodeManager::rotateRight90()
{
rotate(M_PI / 2);
}
void KisNodeManager::shear(double angleX, double angleY)
{
// XXX: implement shear for masks as well
m_d->layerManager->shearLayer(angleX, angleY);
}
void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filterStrategy)
{
KisNodeSP node = activeNode();
KIS_ASSERT_RECOVER_RETURN(node);
m_d->view->image()->scaleNode(node, sx, sy, filterStrategy);
nodesUpdated();
}
void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionName, Qt::Orientation orientation)
{
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(m_d->view->image(), node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
KisProcessingVisitorSP visitor =
new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.end();
nodesUpdated();
}
void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device,
const QString &defaultName,
const QRect &bounds,
qreal xRes,
qreal yRes,
quint8 opacity)
{
KoFileDialog dialog(view->mainWindow(), KoFileDialog::SaveFile, "krita/savenodeasimage");
dialog.setCaption(i18n("Export \"%1\"", defaultName));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export));
QString filename = dialog.url();
if (filename.isEmpty()) return;
KUrl url = KUrl::fromLocalFile(filename);
if (url.isEmpty()) return;
KMimeType::Ptr mime = KMimeType::findByUrl(url);
QString mimefilter = mime->name();
QScopedPointer<KisDocument> d(KisPart::instance()->createDocument());
d->prepareForImport();
KisImageSP dst = new KisImage(d->createUndoStore(),
bounds.width(),
bounds.height(),
device->compositionSourceColorSpace(),
defaultName);
dst->setResolution(xRes, yRes);
d->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity);
paintLayer->paintDevice()->makeCloneFrom(device, bounds);
dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
dst->initialRefreshGraph();
d->setOutputMimeType(mimefilter.toLatin1());
d->exportDocument(url);
}
void KisNodeManager::saveNodeAsImage()
{
KisNodeSP node = activeNode();
if (!node) {
- qWarning() << "BUG: Save Node As Image was called without any node selected";
+ warnKrita << "BUG: Save Node As Image was called without any node selected";
return;
}
KisImageWSP image = m_d->view->image();
QRect saveRect = image->bounds() | node->exactBounds();
KisPaintDeviceSP device = node->paintDevice();
if (!device) {
device = node->projection();
}
m_d->saveDeviceAsImage(device, node->name(),
saveRect,
image->xRes(), image->yRes(),
node->opacity());
}
void KisNodeManager::slotSplitAlphaIntoMask()
{
KisNodeSP node = activeNode();
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice());
KisPaintDeviceSP srcDevice = node->paintDevice();
const KoColorSpace *srcCS = srcDevice->colorSpace();
const QRect processRect =
srcDevice->exactBounds() |
srcDevice->defaultBounds()->bounds();
KisPaintDeviceSP selectionDevice =
new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
m_d->commandsAdapter->beginMacro(kundo2_i18n("Split Alpha into a Mask"));
KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice);
KisSequentialIterator srcIt(srcDevice, processRect);
KisSequentialIterator dstIt(selectionDevice, processRect);
do {
quint8 *srcPtr = srcIt.rawData();
quint8 *alpha8Ptr = dstIt.rawData();
*alpha8Ptr = srcCS->opacityU8(srcPtr);
srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1);
} while (srcIt.nextPixel() && dstIt.nextPixel());
m_d->commandsAdapter->addExtraCommand(transaction.endAndTake());
createNode("KisTransparencyMask", false, selectionDevice);
m_d->commandsAdapter->endMacro();
}
void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers)
{
KisNodeSP node = q->activeNode();
KisNodeSP parentNode = node->parent();
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
if (writeToLayers && !parentNode->hasEditablePaintDevice()) {
QMessageBox::information(view->mainWindow(),
i18nc("@title:window", "Layer %1 is not editable").arg(parentNode->name()),
i18n("Cannot write alpha channel of "
"the parent layer \"%1\".\n"
"The operation will be cancelled.").arg(parentNode->name()));
return;
}
KisPaintDeviceSP dstDevice;
if (writeToLayers) {
KIS_ASSERT_RECOVER_RETURN(parentNode->paintDevice());
dstDevice = parentNode->paintDevice();
} else {
KisPaintDeviceSP copyDevice = parentNode->paintDevice();
if (!copyDevice) {
copyDevice = parentNode->original();
}
dstDevice = new KisPaintDevice(*copyDevice);
}
const KoColorSpace *dstCS = dstDevice->colorSpace();
KisPaintDeviceSP selectionDevice = node->paintDevice();
KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1);
const QRect processRect =
selectionDevice->exactBounds() |
dstDevice->exactBounds() |
selectionDevice->defaultBounds()->bounds();
QScopedPointer<KisTransaction> transaction;
if (writeToLayers) {
commandsAdapter->beginMacro(kundo2_i18n("Write Alpha into a Layer"));
transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice));
}
KisSequentialIterator srcIt(selectionDevice, processRect);
KisSequentialIterator dstIt(dstDevice, processRect);
do {
quint8 *alpha8Ptr = srcIt.rawData();
quint8 *dstPtr = dstIt.rawData();
dstCS->setOpacity(dstPtr, *alpha8Ptr, 1);
} while (srcIt.nextPixel() && dstIt.nextPixel());
if (writeToLayers) {
commandsAdapter->addExtraCommand(transaction->endAndTake());
commandsAdapter->removeNode(node);
commandsAdapter->endMacro();
} else {
KisImageWSP image = view->image();
QRect saveRect = image->bounds();
saveDeviceAsImage(dstDevice, parentNode->name(),
saveRect,
image->xRes(), image->yRes(),
OPACITY_OPAQUE_U8);
}
}
void KisNodeManager::slotSplitAlphaWrite()
{
m_d->mergeTransparencyMaskAsAlpha(true);
}
void KisNodeManager::slotSplitAlphaSaveMerged()
{
m_d->mergeTransparencyMaskAsAlpha(false);
}
diff --git a/krita/ui/kis_painting_assistants_decoration.cpp b/krita/ui/kis_painting_assistants_decoration.cpp
index 545f41240e4..c949d5325c9 100644
--- a/krita/ui/kis_painting_assistants_decoration.cpp
+++ b/krita/ui/kis_painting_assistants_decoration.cpp
@@ -1,212 +1,215 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_painting_assistants_decoration.h"
#include <cfloat>
#include <QList>
#include <QPointF>
#include <klocale.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include "kis_debug.h"
#include "kis_painting_assistant.h"
#include <QPainter>
struct KisPaintingAssistantsDecoration::Private {
QList<KisPaintingAssistant*> assistants;
bool assistantVisible;
bool outlineVisible;
bool snapOnlyOneAssistant;
KisPaintingAssistant* firstAssistant;
bool aFirstStroke;
};
KisPaintingAssistantsDecoration::KisPaintingAssistantsDecoration(QPointer<KisView> parent) :
KisCanvasDecoration("paintingAssistantsDecoration", parent),
d(new Private)
{
setAssistantVisible(true);
setOutlineVisible(true);
d->snapOnlyOneAssistant=true;//turn on by default.
}
KisPaintingAssistantsDecoration::~KisPaintingAssistantsDecoration()
{
qDeleteAll(d->assistants.begin(), d->assistants.end());
delete d;
}
void KisPaintingAssistantsDecoration::addAssistant(KisPaintingAssistant* assistant)
{
if (d->assistants.contains(assistant)) return;
d->assistants.push_back(assistant);
emit assistantChanged();
}
void KisPaintingAssistantsDecoration::removeAssistant(KisPaintingAssistant* assistant)
{
delete assistant;
d->assistants.removeAll(assistant);
emit assistantChanged();
}
void KisPaintingAssistantsDecoration::removeAll()
{
foreach (KisPaintingAssistant* assistant, d->assistants) {
delete assistant;
}
d->assistants.clear();
emit assistantChanged();
}
QPointF KisPaintingAssistantsDecoration::adjustPosition(const QPointF& point, const QPointF& strokeBegin)
{
if (d->assistants.empty()) return point;
if (d->assistants.count() == 1) {
if(d->assistants.first()->snapping()==true){
QPointF newpoint = d->assistants.first()->adjustPosition(point, strokeBegin);
// check for NaN
if (newpoint.x() != newpoint.x()) return point;
return newpoint;
}
}
QPointF best = point;
double distance = DBL_MAX;
//the following tries to find the closest point to stroke-begin. It checks all assistants for the closest point//
if(!d->snapOnlyOneAssistant){
foreach(KisPaintingAssistant* assistant, d->assistants) {
if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on//
QPointF pt = assistant->adjustPosition(point, strokeBegin);
if (pt.x() != pt.x()) continue;
double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y());
if (dist < distance) {
best = pt;
distance = dist;
}
}
}
} else if (d->aFirstStroke==false) {
foreach(KisPaintingAssistant* assistant, d->assistants) {
if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on//
QPointF pt = assistant->adjustPosition(point, strokeBegin);
if (pt.x() != pt.x()) continue;
double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y());
if (dist < distance) {
best = pt;
distance = dist;
d->firstAssistant = assistant;
}
}
}
- } else {
+ } else if(d->firstAssistant) {
+ //make sure there's a first assistant to begin with.//
best = d->firstAssistant->adjustPosition(point, strokeBegin);
+ } else {
+ d->aFirstStroke=false;
}
//this is here to be compatible with the movement in the perspective tool.
qreal dx = point.x() - strokeBegin.x(), dy = point.y() - strokeBegin.y();
if (dx * dx + dy * dy >= 4.0) {
// allow some movement before snapping
d->aFirstStroke=true;
}
return best;
}
void KisPaintingAssistantsDecoration::endStroke()
{
d->aFirstStroke=false;
foreach(KisPaintingAssistant* assistant, d->assistants) {
assistant->endStroke();
}
}
void KisPaintingAssistantsDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas)
{
if (!canvas) {
dbgFile<<"canvas does not exist in painting assistant decoration, you may have passed arguments incorrectly:"<<canvas;
}
foreach(KisPaintingAssistant* assistant, d->assistants) {
assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), outlineVisibility());
}
}
//drawPreview//
QList<KisPaintingAssistantHandleSP> KisPaintingAssistantsDecoration::handles()
{
QList<KisPaintingAssistantHandleSP> hs;
foreach(KisPaintingAssistant* assistant, d->assistants) {
foreach(const KisPaintingAssistantHandleSP handle, assistant->handles()) {
if (!hs.contains(handle)) {
hs.push_back(handle);
}
}
foreach(const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) {
if (!hs.contains(handle)) {
hs.push_back(handle);
}
}
}
return hs;
}
QList<KisPaintingAssistant*> KisPaintingAssistantsDecoration::assistants()
{
return d->assistants;
}
void KisPaintingAssistantsDecoration::setAssistantVisible(bool set)
{
d->assistantVisible=set;
}
void KisPaintingAssistantsDecoration::setOutlineVisible(bool set)
{
d->outlineVisible=set;
}
void KisPaintingAssistantsDecoration::setOnlyOneAssistantSnap(bool assistant)
{
d->snapOnlyOneAssistant = assistant;
}
bool KisPaintingAssistantsDecoration::assistantVisibility()
{
return d->assistantVisible;
}
bool KisPaintingAssistantsDecoration::outlineVisibility()
{
return d->outlineVisible;
}
void KisPaintingAssistantsDecoration::uncache()
{
foreach(KisPaintingAssistant* assistant, d->assistants) {
assistant->uncache();
}
}
void KisPaintingAssistantsDecoration::toggleAssistantVisible()
{
setAssistantVisible(!assistantVisibility());
uncache();
}
void KisPaintingAssistantsDecoration::toggleOutlineVisible()
{
setOutlineVisible(!outlineVisibility());
}
diff --git a/krita/ui/kis_paintop_box.cc b/krita/ui/kis_paintop_box.cc
index 592c70d90ae..b1dac670ad2 100644
--- a/krita/ui/kis_paintop_box.cc
+++ b/krita/ui/kis_paintop_box.cc
@@ -1,1180 +1,1180 @@
/*
* kis_paintop_box.cc - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com)
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
* Copyright (c) 2014 Mohit Goyal <mohit.bits2011@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_box.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QAction>
#include <QPixmap>
#include <kis_debug.h>
#include <kactioncollection.h>
#include <kacceleratormanager.h>
#include <kshortcut.h>
#include <kglobal.h>
#include <kis_icon_utils.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include <KoResourceSelector.h>
#include <KoResourceServerAdapter.h>
#include <KoToolManager.h>
#include <QTemporaryFile>
#include <KoColorSpaceRegistry.h>
#include <kis_paint_device.h>
#include <kis_paintop_registry.h>
#include <kis_paintop_preset.h>
#include <kis_paintop_settings.h>
#include <kis_config_widget.h>
#include <kis_image.h>
#include <kis_node.h>
#include <kis_paintop_config_widget.h>
#include <kis_action.h>
#include "kis_canvas2.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "kis_factory2.h"
#include "kis_canvas_resource_provider.h"
#include "kis_resource_server_provider.h"
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "widgets/kis_popup_button.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_popup.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_chooser_popup.h"
#include "widgets/kis_workspace_chooser.h"
#include "widgets/kis_paintop_list_widget.h"
#include "widgets/kis_slider_spin_box.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_widget_chooser.h"
#include "tool/kis_tool.h"
#include "kis_signals_blocker.h"
typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
: QWidget(parent)
, m_resourceProvider(view->resourceProvider())
, m_optionWidget(0)
, m_toolOptionsPopupButton(0)
, m_brushEditorPopupButton(0)
, m_presetSelectorPopupButton(0)
, m_toolOptionsPopup(0)
, m_viewManager(view)
, m_previousNode(0)
, m_currTabletToolID(KoInputDevice::invalid())
, m_presetsEnabled(true)
, m_blockUpdate(false)
, m_dirtyPresetsEnabled(false)
, m_eraserBrushSizeEnabled(false)
{
Q_ASSERT(view != 0);
KGlobal::dirs()->addResourceType("kis_defaultpresets", "data", "krita/defaultpresets/");
setObjectName(name);
KisConfig cfg;
m_dirtyPresetsEnabled = cfg.useDirtyPresets();
m_eraserBrushSizeEnabled = cfg.useEraserBrushSize();
KAcceleratorManager::setNoAccel(this);
setWindowTitle(i18n("Painter's Toolchest"));
KConfigGroup grp = KGlobal::config()->group("krita").group("Toolbar BrushesAndStuff");
int iconsize = grp.readEntry("IconSize", 32);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton = new KisPopupButton(this);
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings"));
m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize);
}
m_brushEditorPopupButton = new KisPopupButton(this);
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings"));
m_brushEditorPopupButton->setFixedSize(iconsize, iconsize);
m_presetSelectorPopupButton = new KisPopupButton(this);
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset"));
m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton = new QToolButton(this);
m_eraseModeButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton->setCheckable(true);
KisAction* eraseAction = new KisAction(i18n("Set eraser mode"), m_eraseModeButton);
eraseAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser"));
eraseAction->setShortcut(Qt::Key_E);
eraseAction->setCheckable(true);
m_eraseModeButton->setDefaultAction(eraseAction);
m_viewManager->actionCollection()->addAction("erase_action", eraseAction);
eraserBrushSize = 0; // brush size changed when using erase mode
m_reloadButton = new QToolButton(this);
m_reloadButton->setFixedSize(iconsize, iconsize);
m_reloadButton->setCheckable(true);
KisAction* reloadAction = new KisAction(i18n("Reload Original Preset"), m_reloadButton);
reloadAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh"));
m_reloadButton->setDefaultAction(reloadAction);
m_viewManager->actionCollection()->addAction("reload_preset_action", reloadAction);
m_alphaLockButton = new QToolButton(this);
m_alphaLockButton->setFixedSize(iconsize, iconsize);
m_alphaLockButton->setCheckable(true);
KisAction* alphaLockAction = new KisAction(i18n("Preserve Alpha"), m_alphaLockButton);
alphaLockAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
alphaLockAction->setIcon(KisIconUtils::loadIcon("transparency-unlocked"));
alphaLockAction->setCheckable(true);
m_alphaLockButton->setDefaultAction(alphaLockAction);
m_viewManager->actionCollection()->addAction("preserve_alpha", alphaLockAction);
m_hMirrorButton = new QToolButton(this);
m_hMirrorButton->setFixedSize(iconsize, iconsize);
m_hMirrorButton->setCheckable(true);
m_hMirrorAction = new KisAction(i18n("Set horizontal mirror mode"), m_hMirrorButton);
m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
m_hMirrorAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
m_hMirrorAction->setCheckable(true);
m_hMirrorButton->setDefaultAction(m_hMirrorAction);
m_viewManager->actionCollection()->addAction("hmirror_action", m_hMirrorAction);
m_vMirrorButton = new QToolButton(this);
m_vMirrorButton->setFixedSize(iconsize, iconsize);
m_vMirrorButton->setCheckable(true);
m_vMirrorAction = new KisAction(i18n("Set vertical mirror mode"), m_vMirrorButton);
m_vMirrorAction->setActivationFlags(KisAction::ACTIVE_DEVICE);
m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical"));
m_vMirrorAction->setCheckable(true);
m_vMirrorButton->setDefaultAction(m_vMirrorAction);
m_viewManager->actionCollection()->addAction("vmirror_action", m_vMirrorAction);
const bool sliderLabels = cfg.sliderLabels();
int sliderWidth;
if (sliderLabels) {
sliderWidth = 150 * logicalDpiX() / 96;
}
else {
sliderWidth = 120 * logicalDpiX() / 96;
}
for (int i = 0; i < 3; ++i) {
m_sliderChooser[i] = new KisWidgetChooser(i + 1);
KisDoubleSliderSpinBox* slOpacity;
KisDoubleSliderSpinBox* slFlow;
KisDoubleSliderSpinBox* slSize;
if (sliderLabels) {
slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity");
slFlow = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("flow");
slSize = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("size");
slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:")));
slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:")));
slSize->setPrefix(QString("%1 ").arg(i18n("Size:")));
}
else {
slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity", i18n("Opacity:"));
slFlow = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("flow", i18n("Flow:"));
slSize = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("size", i18n("Size:"));
}
slOpacity->setRange(0.0, 1.0, 2);
slOpacity->setValue(1.0);
slOpacity->setSingleStep(0.05);
slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width()));
slOpacity->setFixedHeight(iconsize);
slOpacity->setBlockUpdateSignalOnDrag(true);
slFlow->setRange(0.0, 1.0, 2);
slFlow->setValue(1.0);
slFlow->setSingleStep(0.05);
slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
slFlow->setFixedHeight(iconsize);
slFlow->setBlockUpdateSignalOnDrag(true);
slSize->setRange(0, 1000, 2);
slSize->setValue(100);
slSize->setSingleStep(1);
slSize->setExponentRatio(3.0);
slSize->setSuffix(" px");
slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width()));
slSize->setFixedHeight(iconsize);
slSize->setBlockUpdateSignalOnDrag(true);
m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1));
}
m_cmbCompositeOp = new KisCompositeOpComboBox();
m_cmbCompositeOp->setFixedHeight(iconsize);
foreach(KAction * a, m_cmbCompositeOp->blendmodeActions()) {
m_viewManager->actionCollection()->addAction(a->text(), a);
}
m_workspaceWidget = new KisPopupButton(this);
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_workspaceWidget->setToolTip(i18n("Choose workspace"));
m_workspaceWidget->setFixedSize(iconsize, iconsize);
m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view));
QHBoxLayout* baseLayout = new QHBoxLayout(this);
m_paintopWidget = new QWidget(this);
baseLayout->addWidget(m_paintopWidget);
baseLayout->setSpacing(4);
baseLayout->setContentsMargins(0, 0, 0, 0);
m_layout = new QHBoxLayout(m_paintopWidget);
if (!cfg.toolOptionsInDocker()) {
m_layout->addWidget(m_toolOptionsPopupButton);
}
m_layout->addWidget(m_brushEditorPopupButton);
m_layout->addWidget(m_presetSelectorPopupButton);
m_layout->setSpacing(4);
m_layout->setContentsMargins(0, 0, 0, 0);
QWidget* compositeActions = new QWidget(this);
QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions);
compositeLayout->addWidget(m_cmbCompositeOp);
compositeLayout->addWidget(m_eraseModeButton);
compositeLayout->addWidget(m_alphaLockButton);
compositeLayout->setSpacing(4);
compositeLayout->setContentsMargins(0, 0, 0, 0);
compositeLayout->addWidget(m_reloadButton);
KAction* action;
action = new KAction(i18n("Brush composite"), this);
view->actionCollection()->addAction("composite_actions", action);
action->setDefaultWidget(compositeActions);
action = new KAction(i18n("Brush option slider 1"), this);
view->actionCollection()->addAction("brushslider1", action);
action->setDefaultWidget(m_sliderChooser[0]);
connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons()));
action = new KAction(i18n("Brush option slider 2"), this);
view->actionCollection()->addAction("brushslider2", action);
action->setDefaultWidget(m_sliderChooser[1]);
connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons()));
action = new KAction(i18n("Brush option slider 3"), this);
view->actionCollection()->addAction("brushslider3", action);
action->setDefaultWidget(m_sliderChooser[2]);
connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons()));
action = new KAction(i18n("Next Favourite Preset"), this);
view->actionCollection()->addAction("next_favorite_preset", action);
action->setShortcut(KShortcut(Qt::Key_Comma));
connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset()));
action = new KAction(i18n("Previous Favourite Preset"), this);
view->actionCollection()->addAction("previous_favorite_preset", action);
action->setShortcut(KShortcut(Qt::Key_Period));
connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset()));
action = new KAction(i18n("Switch to Previous Preset"), this);
view->actionCollection()->addAction("previous_preset", action);
action->setShortcut(KShortcut(Qt::Key_Slash));
connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset()));
if (!cfg.toolOptionsInDocker()) {
action = new KAction(i18n("Show Tool Options"), this);
view->actionCollection()->addAction("show_tool_options", action);
action->setShortcut(Qt::Key_Backslash);
connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget()));
}
action = new KAction(i18n("Show Brush Editor"), this);
view->actionCollection()->addAction("show_brush_editor", action);
action->setShortcut(Qt::Key_F5);
connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget()));
action = new KAction(i18n("Show Brush Presets"), this);
view->actionCollection()->addAction("show_brush_presets", action);
action->setShortcut(Qt::Key_F6);
connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget()));
QWidget* mirrorActions = new QWidget(this);
QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions);
mirrorLayout->addWidget(m_hMirrorButton);
mirrorLayout->addWidget(m_vMirrorButton);
mirrorLayout->setSpacing(4);
mirrorLayout->setContentsMargins(0, 0, 0, 0);
action = new KAction(i18n("Mirror"), this);
view->actionCollection()->addAction("mirror_actions", action);
action->setDefaultWidget(mirrorActions);
action = new KAction(i18n("Workspaces"), this);
view->actionCollection()->addAction("workspaces", action);
action->setDefaultWidget(m_workspaceWidget);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopup = new KisToolOptionsPopup();
m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup);
m_toolOptionsPopup->switchDetached(false);
}
m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider);
m_brushEditorPopupButton->setPopupWidget(m_presetsPopup);
m_presetsPopup->switchDetached(false);
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(slotUpdateThemedIcons()));
m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup();
m_presetsChooserPopup->setFixedSize(500, 600);
m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup);
m_prevCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
slotNodeChanged(view->activeNode());
// Get all the paintops
QList<QString> keys = KisPaintOpRegistry::instance()->keys();
QList<KisPaintOpFactory*> factoryList;
foreach(const QString & paintopId, keys) {
factoryList.append(KisPaintOpRegistry::instance()->get(paintopId));
}
m_presetsPopup->setPaintOpList(factoryList);
connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString)));
connect(m_presetsPopup , SIGNAL(savePresetClicked()) , SLOT(slotSaveActivePreset()));
connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset()));
connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*)));
connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset()));
connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool)));
connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP)));
connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int)));
connect(eraseAction , SIGNAL(triggered(bool)) , SLOT(slotToggleEraseMode(bool)));
connect(alphaLockAction , SIGNAL(triggered(bool)) , SLOT(slotToggleAlphaLockMode(bool)));
connect(m_hMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotHorizontalMirrorChanged(bool)));
connect(m_vMirrorAction , SIGNAL(triggered(bool)) , SLOT(slotVerticalMirrorChanged(bool)));
connect(reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
//Needed to connect canvas to favorite resource manager
connect(m_viewManager->resourceProvider(), SIGNAL(sigOpacityChanged(qreal)), SLOT(slotOpacityChanged(qreal)));
connect(m_viewManager->resourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode()));
m_favoriteResourceManager = new KisFavoriteResourceManager(this);
connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor)));
connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor)));
// cold initialization
m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor());
m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor());
connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool)));
connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon()));
slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice());
}
KisPaintopBox::~KisPaintopBox()
{
KisConfig cfg;
QMapIterator<TabletToolID, TabletToolData> iter(m_tabletToolMap);
while (iter.hasNext()) {
iter.next();
if ((iter.key().pointer) == QTabletEvent::Eraser) {
cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
else {
cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
}
// Do not delete the widget, since it it is global to the application, not owned by the view
m_presetsPopup->setPaintOpSettingsWidget(0);
qDeleteAll(m_paintopOptionWidgets);
delete m_favoriteResourceManager;
}
void KisPaintopBox::restoreResource(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast<KisPaintOpPreset*>(resource);
if (preset) {
setCurrentPaintop(preset->paintOp(), preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::newOptionWidgets(const QList<QPointer<QWidget> > &optionWidgetList)
{
if (m_toolOptionsPopup) {
m_toolOptionsPopup->newOptionWidgets(optionWidgetList);
}
}
void KisPaintopBox::resourceSelected(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast<KisPaintOpPreset*>(resource);
if (preset) {
if (!preset->settings()->isLoadable())
return;
setCurrentPaintopAndReload(preset->paintOp(), preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
QPixmap KisPaintopBox::paintopPixmap(const KoID& paintop)
{
QString pixmapName = KisPaintOpRegistry::instance()->pixmap(paintop);
if (pixmapName.isEmpty())
return QPixmap();
return QPixmap(KGlobal::dirs()->findResource("kis_images", pixmapName));
}
KoID KisPaintopBox::currentPaintop()
{
return m_resourceProvider->currentPreset()->paintOp();
}
void KisPaintopBox::setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset)
{
if (!m_dirtyPresetsEnabled) {
KisSignalsBlocker blocker(m_optionWidget);
if (!preset->load()) {
- qWarning() << "failed to load the preset.";
+ warnKrita << "failed to load the preset.";
}
}
setCurrentPaintop(paintop, preset);
}
void KisPaintopBox::setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset)
{
if (m_resourceProvider->currentPreset()) {
m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset());
if (m_optionWidget) {
m_optionWidget->disconnect(this);
m_optionWidget->hide();
}
m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = m_resourceProvider->currentPreset();
m_tabletToolMap[m_currTabletToolID].preset = m_resourceProvider->currentPreset();
m_tabletToolMap[m_currTabletToolID].paintOpID = m_resourceProvider->currentPreset()->paintOp();
}
preset = (!preset) ? activePreset(paintop) : preset;
Q_ASSERT(preset && preset->settings());
if (!m_paintopOptionWidgets.contains(paintop))
m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this);
m_optionWidget = m_paintopOptionWidgets[paintop];
preset->settings()->setOptionsWidget(m_optionWidget);
m_optionWidget->setImage(m_viewManager->image());
m_optionWidget->setNode(m_viewManager->activeNode());
m_optionWidget->setConfiguration(preset->settings());
m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget);
Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton);
connect(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotUpdatePreset()));
connect(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfiguration*)));
connect(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotDropLockedOption(KisPropertiesConfiguration*)));
connect(m_optionWidget, SIGNAL(sigConfigurationItemChanged()), this, SLOT(slotConfigurationItemChanged()));
KisPaintOpFactory* paintOp = KisPaintOpRegistry::instance()->get(paintop.id());
QString pixFilename = KGlobal::dirs()->findResource("kis_images", paintOp->pixmap());
m_brushEditorPopupButton->setIcon(QIcon(pixFilename));
m_resourceProvider->setPaintOpPreset(preset);
m_presetsPopup->setCurrentPaintOp(paintop.id());
if (m_presetsPopup->currentPaintOp() != paintop.id()) {
// Must change the paintop as the current one is not supported
// by the new colorspace.
- kWarning() << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
+ dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
}
/**
* We will get more update signals from the configuration widgets
* but they might be delayed by some internal deferring timers,
* so just call the slot directly
*/
slotUpdatePreset();
}
KoID KisPaintopBox::defaultPaintOp()
{
return KoID("paintbrush");
}
KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp)
{
QString defaultName = paintOp.id() + ".kpp";
QString path = KGlobal::dirs()->findResource("kis_defaultpresets", defaultName);
KisPaintOpPresetSP preset = new KisPaintOpPreset(path);
if (!preset->load()) {
preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp);
}
Q_ASSERT(preset);
Q_ASSERT(preset->valid());
return preset;
}
KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp)
{
if (m_paintOpPresetMap[paintOp] == 0) {
m_paintOpPresetMap[paintOp] = defaultPreset(paintOp);
}
return m_paintOpPresetMap[paintOp];
}
void KisPaintopBox::updateCompositeOp(QString compositeOpID, bool localUpdate)
{
if (!m_optionWidget) return;
KisSignalsBlocker blocker(m_optionWidget);
KisNodeSP node = m_resourceProvider->currentNode();
if (node && node->paintDevice()) {
if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID))
compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
m_cmbCompositeOp->blockSignals(true);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
m_cmbCompositeOp->blockSignals(false);
m_eraseModeButton->defaultAction()->blockSignals(true);
m_eraseModeButton->blockSignals(true);
m_eraseModeButton->setChecked(compositeOpID == COMPOSITE_ERASE);
m_eraseModeButton->defaultAction()->setChecked(compositeOpID == COMPOSITE_ERASE);
m_eraseModeButton->blockSignals(false);
m_eraseModeButton->defaultAction()->blockSignals(false);
if (compositeOpID != m_currCompositeOpID) {
m_resourceProvider->currentPreset()->settings()->setPaintOpCompositeOp(compositeOpID);
m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data());
if (!localUpdate)
m_resourceProvider->setCurrentCompositeOp(compositeOpID);
m_prevCompositeOpID = m_currCompositeOpID;
m_currCompositeOpID = compositeOpID;
}
}
}
void KisPaintopBox::setWidgetState(int flags)
{
if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP);
}
if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) {
m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS);
m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS);
}
for (int i = 0; i < 3; ++i) {
if (flags & (ENABLE_OPACITY | DISABLE_OPACITY))
m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY);
if (flags & (ENABLE_FLOW | DISABLE_FLOW))
m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW);
if (flags & (ENABLE_SIZE | DISABLE_SIZE))
m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE);
}
}
void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
{
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget<KisDoubleSliderSpinBox>(sliderID);
slider->blockSignals(true);
slider->setValue(value);
slider->blockSignals(false);
}
}
void KisPaintopBox::slotSetPaintop(const QString& paintOpId)
{
if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) {
KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name());
setCurrentPaintop(id);
}
}
void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice)
{
TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice);
if (toolData == m_tabletToolMap.end()) {
KisConfig cfg;
KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(false);
KisPaintOpPresetSP preset;
if (inputDevice.pointer() == QTabletEvent::Eraser) {
preset = rserver->resourceByName(cfg.readEntry<QString>(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), "Eraser_circle"));
}
else {
preset = rserver->resourceByName(cfg.readEntry<QString>(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), "Basic_tip_default"));
}
if (!preset) {
preset = rserver->resourceByName("Basic_tip_default");
}
if (preset) {
setCurrentPaintop(preset->paintOp(), preset);
}
}
else {
setCurrentPaintop(toolData->paintOpID, toolData->preset);
}
m_currTabletToolID = TabletToolID(inputDevice);
}
void KisPaintopBox::slotCanvasResourceChanged(int /*key*/, const QVariant& /*v*/)
{
if (m_viewManager) {
sender()->blockSignals(true);
KisPaintOpPresetSP preset = m_viewManager->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) {
QString compositeOp = preset->settings()->getString("CompositeOp");
updateCompositeOp(compositeOp);
resourceSelected(preset.data());
}
m_presetsChooserPopup->canvasResourceChanged(preset.data(), preset);
if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) {
QString compositeOp = m_resourceProvider->currentCompositeOp();
m_cmbCompositeOp->blockSignals(true);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOp));
m_cmbCompositeOp->blockSignals(false);
m_eraseModeButton->defaultAction()->blockSignals(true);
m_eraseModeButton->blockSignals(true);
m_eraseModeButton->setChecked(compositeOp == COMPOSITE_ERASE);
m_eraseModeButton->defaultAction()->setChecked(compositeOp == COMPOSITE_ERASE);
m_eraseModeButton->blockSignals(false);
m_eraseModeButton->defaultAction()->blockSignals(false);
}
sender()->blockSignals(false);
}
}
void KisPaintopBox::slotSaveActivePreset()
{
KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset();
if (!curPreset)
return;
m_favoriteResourceManager->setBlockUpdates(true);
KisPaintOpPresetSP newPreset = curPreset->clone();
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QString saveLocation = rServer->saveLocation();
QString presetName = m_presetsPopup->getPresetName();
QString presetFilename = saveLocation + presetName + newPreset->defaultFileExtension();
QStringList tags;
KisPaintOpPresetSP resource = rServer->resourceByName(presetName);
if (resource) {
tags = rServer->assignedTagsList(resource.data());
rServer->removeResourceAndBlacklist(resource);
}
newPreset->setImage(m_presetsPopup->cutOutOverlay());
newPreset->setFilename(presetFilename);
newPreset->setName(presetName);
newPreset->setPresetDirty(false);
rServer->addResource(newPreset);
foreach(const QString & tag, tags) {
rServer->addTag(newPreset.data(), tag);
}
// HACK ALERT! the server does not notify the observers
// automatically, so we need to call theupdate manually!
rServer->tagCategoryMembersChanged();
restoreResource(newPreset.data());
m_favoriteResourceManager->setBlockUpdates(false);
}
void KisPaintopBox::slotUpdatePreset()
{
if (!m_resourceProvider->currentPreset()) return;
// block updates of avoid some over updating of the option widget
m_blockUpdate = true;
setSliderValue("size", m_resourceProvider->currentPreset()->settings()->paintOpSize().width());
{
qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity();
m_resourceProvider->setOpacity(opacity);
setSliderValue("opacity", opacity);
setWidgetState(ENABLE_OPACITY);
}
{
setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow());
setWidgetState(ENABLE_FLOW);
}
{
updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp());
setWidgetState(ENABLE_COMPOSITEOP);
}
m_blockUpdate = false;
}
void KisPaintopBox::slotSetupDefaultPreset()
{
KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp());
preset->settings()->setOptionsWidget(m_optionWidget);
m_optionWidget->setConfiguration(preset->settings());
m_optionWidget->writeConfiguration(const_cast<KisPaintOpSettings*>(preset->settings().data()));
}
void KisPaintopBox::slotNodeChanged(const KisNodeSP node)
{
if (m_previousNode.isValid() && m_previousNode->paintDevice())
disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
// Reconnect colorspace change of node
if (node && node->paintDevice()) {
connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID);
m_previousNode = node;
slotColorSpaceChanged(node->colorSpace());
}
if (m_optionWidget) {
m_optionWidget->setNode(node);
}
}
void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace)
{
m_cmbCompositeOp->validate(colorSpace);
}
void KisPaintopBox::slotToggleEraseMode(bool checked)
{
if (checked)
{
updateCompositeOp(COMPOSITE_ERASE);
//add an option to enable eraser brush size
if (m_eraserBrushSizeEnabled==true)
{
// remember brush size. set the eraser size to the normal brush size if not set
normalBrushSize = m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size")->value();
if (qFuzzyIsNull(eraserBrushSize))
eraserBrushSize = normalBrushSize;
}
else
{
normalBrushSize = eraserBrushSize;
eraserBrushSize = m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size")->value();
}
}
else
{
updateCompositeOp(m_prevCompositeOpID);
if (m_eraserBrushSizeEnabled==true)
{
// save eraser brush size as eraserBrushSize (they are all the same, so just grab the first one)
eraserBrushSize = m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size")->value();
}
else
{
normalBrushSize = m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size")->value();
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal updateSize = checked ? eraserBrushSize : normalBrushSize;
m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size")->setValue(updateSize);
m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("size")->setValue(updateSize);
m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("size")->setValue(updateSize);
toggleHighlightedButton(m_eraseModeButton);
}
void KisPaintopBox::slotSetCompositeMode(int index)
{
Q_UNUSED(index);
if (m_resourceProvider->currentPreset()->settings()->hasProperty("CompositeOp")) {
QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id();
updateCompositeOp(compositeOp);
}
}
void KisPaintopBox::slotHorizontalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorHorizontal(value);
toggleHighlightedButton(m_hMirrorButton);
}
void KisPaintopBox::slotVerticalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorVertical(value);
toggleHighlightedButton(m_vMirrorButton);
}
void KisPaintopBox::sliderChanged(int n)
{
if (!m_optionWidget) // widget will not exist if the are no documents open
return;
KisSignalsBlocker blocker(m_optionWidget);
m_optionWidget->writeConfiguration(const_cast<KisPaintOpSettings*>(m_resourceProvider->currentPreset()->settings().data()));
qreal opacity = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("opacity")->value();
qreal flow = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("flow")->value();
qreal size = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("size")->value();
setSliderValue("opacity", opacity);
setSliderValue("flow" , flow);
setSliderValue("size" , size);
if (m_presetsEnabled) {
// IMPORTANT: set the PaintOp size before setting the other properties
// it wont work the other way
qreal sizeDiff = size - m_resourceProvider->currentPreset()->settings()->paintOpSize().width();
m_resourceProvider->currentPreset()->settings()->changePaintOpSize(sizeDiff, 0);
m_resourceProvider->currentPreset()->settings()->setPaintOpOpacity(opacity);
m_resourceProvider->currentPreset()->settings()->setPaintOpFlow(flow);
KisLockedPropertiesProxy *propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings());
propertiesProxy->setProperty("OpacityValue", opacity);
propertiesProxy->setProperty("FlowValue", flow);
delete propertiesProxy;
m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data());
}
m_resourceProvider->setOpacity(opacity);
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
}
void KisPaintopBox::slotSlider1Changed()
{
sliderChanged(0);
}
void KisPaintopBox::slotSlider2Changed()
{
sliderChanged(1);
}
void KisPaintopBox::slotSlider3Changed()
{
sliderChanged(2);
}
void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId)
{
Q_UNUSED(canvas);
Q_UNUSED(toolId);
if (!m_viewManager->canvasBase()) return;
QString id = KoToolManager::instance()->activeToolId();
KisTool* tool = dynamic_cast<KisTool*>(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id));
if (tool) {
int flags = tool->flags();
if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) {
setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY);
} else {
setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY);
}
if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) {
setWidgetState(ENABLE_PRESETS | ENABLE_SIZE | ENABLE_FLOW);
slotUpdatePreset();
m_presetsEnabled = true;
} else {
setWidgetState(DISABLE_PRESETS | DISABLE_SIZE | DISABLE_FLOW);
m_presetsEnabled = false;
}
} else setWidgetState(DISABLE_ALL);
}
void KisPaintopBox::slotOpacityChanged(qreal opacity)
{
if (m_blockUpdate) {
return;
}
m_blockUpdate = true;
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox *opacitySlider = m_sliderChooser[i]->getWidget<KisDoubleSliderSpinBox>("opacity");
opacitySlider->blockSignals(true);
opacitySlider->setValue(opacity);
opacitySlider->blockSignals(false);
}
if (m_presetsEnabled) {
m_resourceProvider->currentPreset()->settings()->setPaintOpOpacity(opacity);
m_optionWidget->setConfiguration(m_resourceProvider->currentPreset()->settings().data());
}
m_blockUpdate = false;
}
void KisPaintopBox::slotPreviousFavoritePreset()
{
if (!m_favoriteResourceManager) return;
int i = 0;
foreach (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
if (m_resourceProvider->currentPreset() && m_resourceProvider->currentPreset()->name() == preset->name()) {
if (i > 0) {
m_favoriteResourceManager->slotChangeActivePaintop(i - 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1);
}
return;
}
i++;
}
}
void KisPaintopBox::slotNextFavoritePreset()
{
if (!m_favoriteResourceManager) return;
int i = 0;
foreach (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
if (m_resourceProvider->currentPreset()->name() == preset->name()) {
if (i < m_favoriteResourceManager->numFavoritePresets() - 1) {
m_favoriteResourceManager->slotChangeActivePaintop(i + 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(0);
}
return;
}
i++;
}
}
void KisPaintopBox::slotSwitchToPreviousPreset()
{
if (m_resourceProvider->previousPreset()) {
setCurrentPaintop(m_resourceProvider->previousPreset()->paintOp(), m_resourceProvider->previousPreset());
}
}
void KisPaintopBox::slotUnsetEraseMode()
{
if (m_currCompositeOpID == COMPOSITE_ERASE) {
updateCompositeOp(m_prevCompositeOpID);
}
}
void KisPaintopBox::slotToggleAlphaLockMode(bool checked)
{
if (checked) {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked"));
} else {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked"));
}
toggleHighlightedButton(m_alphaLockButton);
m_resourceProvider->setGlobalAlphaLock(checked);
}
void KisPaintopBox::toggleHighlightedButton(QToolButton* m_tool)
{
QPalette p = palette();
if (m_tool->isChecked()) {
QPalette palette_highlight(p);
palette_highlight.setColor(QPalette::Button, p.color(QPalette::Highlight));
m_tool->setPalette(palette_highlight);
}
else {
m_tool->setPalette(p);
}
}
void KisPaintopBox::slotReloadPreset()
{
KisSignalsBlocker blocker(m_optionWidget);
//Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading.
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name());
if (preset) {
preset->load();
preset->settings()->setOptionsWidget(m_optionWidget);
m_optionWidget->setConfiguration(preset->settings());
m_presetsPopup->resourceSelected(preset.data());
}
slotUpdatePreset();
}
void KisPaintopBox::slotConfigurationItemChanged() // Called only when UI is changed and not when preset is changed
{
m_optionWidget->writeConfiguration(const_cast<KisPaintOpSettings*>(m_resourceProvider->currentPreset()->settings().data()));
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
m_presetsPopup->updateViewSettings();
}
void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p)
{
QMapIterator<QString, QVariant> i(p->getProperties());
while (i.hasNext()) {
i.next();
m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value()));
if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) {
m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous");
}
}
slotConfigurationItemChanged();
}
void KisPaintopBox::slotDropLockedOption(KisPropertiesConfiguration* p)
{
KisSignalsBlocker blocker(m_optionWidget);
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
KisPaintOpPreset::DirtyStateSaver dirtySaver(preset.data());
QMapIterator<QString, QVariant> i(p->getProperties());
while (i.hasNext()) {
i.next();
if (preset->settings()->hasProperty(i.key() + "_previous")) {
preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous"));
preset->settings()->removeProperty(i.key() + "_previous");
}
}
m_optionWidget->setConfiguration(preset->settings());
}
slotUpdatePreset();
}
void KisPaintopBox::slotDirtyPresetToggled(bool value)
{
if (!value) {
slotReloadPreset();
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
m_presetsPopup->updateViewSettings();
}
m_dirtyPresetsEnabled = value;
KisConfig cfg;
cfg.setUseDirtyPresets(m_dirtyPresetsEnabled);
}
void KisPaintopBox::slotEraserBrushSizeToggled(bool value)
{
m_eraserBrushSizeEnabled = value;
KisConfig cfg;
cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled);
}
void KisPaintopBox::slotUpdateSelectionIcon()
{
m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical"));
KisConfig cfg;
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
}
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
}
diff --git a/krita/ui/kis_popup_palette.cpp b/krita/ui/kis_popup_palette.cpp
index 91e51dd4157..7ac1a2bbe9c 100644
--- a/krita/ui/kis_popup_palette.cpp
+++ b/krita/ui/kis_popup_palette.cpp
@@ -1,561 +1,561 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman <shicmap@gmail.com>
Copyright 2011 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kis_config.h"
#include "kis_popup_palette.h"
#include "kis_paintop_box.h"
#include "kis_favorite_resource_manager.h"
#include "KoIcon.h"
#include "kis_paintop_preset.h"
#include "kis_resource_server_provider.h"
#include <KoTriangleColorSelector.h>
#include "KoColorSpaceRegistry.h"
#include <kis_types.h>
#include <QtGui>
-#include <QDebug>
+#include <kis_debug.h>
#include <QQueue>
#include <QMenu>
#include <QWhatsThis>
#include <math.h>
#include "kis_signal_compressor.h"
#define brushInnerRadius 94.0
#define brushOuterRadius 145.0
#define colorInnerRadius 72.0
#define colorOuterRadius 92.0
#define brushRadius (brushInnerRadius+brushOuterRadius)/2
class PopupColorTriangle : public KoTriangleColorSelector
{
public:
PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent)
: KoTriangleColorSelector(displayRenderer, parent)
, m_dragging(false) {
}
virtual ~PopupColorTriangle() {}
void tabletEvent(QTabletEvent* event) {
event->accept();
QMouseEvent* mouseEvent = 0;
switch (event->type()) {
case QEvent::TabletPress:
mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(),
Qt::LeftButton, Qt::LeftButton, event->modifiers());
m_dragging = true;
mousePressEvent(mouseEvent);
break;
case QEvent::TabletMove:
mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(),
(m_dragging) ? Qt::LeftButton : Qt::NoButton,
(m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers());
mouseMoveEvent(mouseEvent);
break;
case QEvent::TabletRelease:
mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
m_dragging = false;
mouseReleaseEvent(mouseEvent);
break;
default: break;
}
delete mouseEvent;
}
private:
bool m_dragging;
};
KisPopupPalette::KisPopupPalette(KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint)
, m_resourceManager(manager)
, m_triangleColorSelector(0)
, m_timer(0)
, m_displayRenderer(displayRenderer)
, m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE))
{
m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this);
m_triangleColorSelector->move(77, 77);
m_triangleColorSelector->resize(136, 136);
m_triangleColorSelector->setVisible(true);
setAutoFillBackground(true);
setAttribute(Qt::WA_ContentsPropagated, true);
//setAttribute(Qt::WA_TranslucentBackground, true);
connect(m_triangleColorSelector, SIGNAL(realColorChanged(KoColor)),
m_colorChangeCompressor, SLOT(start()));
connect(m_colorChangeCompressor, SIGNAL(timeout()),
SLOT(slotEmitColorChanged()));
connect(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)),
SLOT(slotExternalFgColorChanged(KoColor)));
connect(this, SIGNAL(sigChangefGColor(KoColor)),
m_resourceManager, SIGNAL(sigSetFGColor(KoColor)));
connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int)));
connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int)));
connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int)));
connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate()));
connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide()));
// This is used to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer()));
connect(m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor()));
connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool)));
setMouseTracking(true);
setHoveredPreset(-1);
setHoveredColor(-1);
setSelectedColor(-1);
setVisible(true);
setVisible(false);
}
void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color)
{
m_triangleColorSelector->setRealColor(color);
}
void KisPopupPalette::slotEmitColorChanged()
{
if (isVisible()) {
update();
emit sigChangefGColor(m_triangleColorSelector->realColor());
}
}
//setting KisPopupPalette properties
int KisPopupPalette::hoveredPreset() const
{
return m_hoveredPreset;
}
void KisPopupPalette::setHoveredPreset(int x)
{
m_hoveredPreset = x;
}
int KisPopupPalette::hoveredColor() const
{
return m_hoveredColor;
}
void KisPopupPalette::setHoveredColor(int x)
{
m_hoveredColor = x;
}
int KisPopupPalette::selectedColor() const
{
return m_selectedColor;
}
void KisPopupPalette::setSelectedColor(int x)
{
m_selectedColor = x;
}
void KisPopupPalette::slotTriggerTimer()
{
m_timer->start(750);
}
void KisPopupPalette::slotEnableChangeFGColor()
{
emit sigEnableChangeFGColor(true);
}
void KisPopupPalette::showPopupPalette(const QPoint &p)
{
if (!isVisible() && parentWidget()) {
QSize parentSize(parentWidget()->size());
QPoint pointPalette(p.x() - width() / 2, p.y() - height() / 2);
//setting offset point in case the widget is shown outside of canvas region
int offsetX = 0, offsetY = 0;
if ((offsetX = pointPalette.x() + width() - parentSize.width()) > 0 || (offsetX = pointPalette.x()) < 0) {
pointPalette.setX(pointPalette.x() - offsetX);
}
if ((offsetY = pointPalette.y() + height() - parentSize.height()) > 0 || (offsetY = pointPalette.y()) < 0) {
pointPalette.setY(pointPalette.y() - offsetY);
}
move(pointPalette);
}
showPopupPalette(!isVisible());
}
void KisPopupPalette::showPopupPalette(bool show)
{
if (show) {
emit sigEnableChangeFGColor(!show);
QApplication::setOverrideCursor(Qt::ArrowCursor);
} else {
emit sigTriggerTimer();
QApplication::restoreOverrideCursor();
}
setVisible(show);
}
//redefinition of setVariable function to change the scope to private
void KisPopupPalette::setVisible(bool b)
{
QWidget::setVisible(b);
}
QSize KisPopupPalette::sizeHint() const
{
return QSize(290, 290);
}
void KisPopupPalette::resizeEvent(QResizeEvent*)
{
int side = qMin(width(), height());
int fgball = 66;
int sball = 40;
QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side, side, QRegion::Ellipse);
maskedRegion = maskedRegion.united( QRegion(50 - fgball / 2, 32 - fgball / 2 , fgball, fgball, QRegion::Ellipse) );
maskedRegion = maskedRegion.united( QRegion(24 - (fgball - 20 ) / 2, 60 - (fgball - 20 ) / 2 , (fgball - 20 ), (fgball - 20 ), QRegion::Ellipse) );
maskedRegion = maskedRegion.united( QRegion(width() / 2 + side / 2 - sball, height() / 2 + side / 2 - sball, sball , sball, QRegion::Ellipse) );
setMask(maskedRegion);
}
void KisPopupPalette::paintEvent(QPaintEvent* e)
{
float rotationAngle = 0.0;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(e->rect(), palette().brush(QPalette::Window));
painter.translate(width() / 2, height() / 2);
//painting background color
QPainterPath bgColor;
bgColor.addEllipse(QPoint(-width() / 2 + 24, -height() / 2 + 60), 20, 20);
painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor()));
painter.drawPath(bgColor);
//painting foreground color
QPainterPath fgColor;
fgColor.addEllipse(QPoint(-width() / 2 + 50, -height() / 2 + 32), 30, 30);
painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->realColor()));
painter.drawPath(fgColor);
//painting favorite brushes
QList<QImage> images(m_resourceManager->favoritePresetImages());
//painting favorite brushes pixmap/icon
QPainterPath path;
for (int pos = 0; pos < images.size(); pos++) {
painter.save();
path = pathFromPresetIndex(pos);
painter.setClipPath(path);
QRect bounds = path.boundingRect().toAlignedRect();
painter.drawImage(bounds.topLeft(), images.at(pos).scaled(bounds.size(), Qt::KeepAspectRatioByExpanding));
painter.restore();
}
if (hoveredPreset() > -1) {
path = pathFromPresetIndex(hoveredPreset());
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(3);
painter.setPen(pen);
painter.drawPath(path);
}
//painting recent colors
painter.setPen(Qt::NoPen);
rotationAngle = -360.0 / m_resourceManager->recentColorsTotal();
KoColor kocolor;
for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) {
QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
//accessing recent color of index pos
kocolor = m_resourceManager->recentColorAt(pos);
painter.fillPath(path, m_displayRenderer->toQColor(kocolor));
painter.drawPath(path);
painter.rotate(rotationAngle);
}
painter.setBrush(Qt::transparent);
if (m_resourceManager->recentColorsTotal() == 0) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius));
painter.setPen(QPen(palette().color(QPalette::Window).darker(130), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
painter.drawPath(path_ColorDonut);
}
//painting hovered color
if (hoveredColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(hoveredColor() * -1 * rotationAngle);
}
}
//painting selected color
if (selectedColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, colorInnerRadius, colorOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(colorInnerRadius, colorOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(selectedColor() * -1 * rotationAngle);
}
}
int side = qMin(width(), height());
QPixmap settingIcon = themedIcon("configure").pixmap(QSize(22,22));
painter.drawPixmap(side / 2 - 40 + 9, side / 2 - 40 + 9, settingIcon);
}
QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius)
{
QPainterPath path;
path.addEllipse(QPointF(x, y), outer_radius, outer_radius);
path.addEllipse(QPointF(x, y), inner_radius, inner_radius);
path.setFillRule(Qt::OddEvenFill);
return path;
}
QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit)
{
QPainterPath path;
path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit));
path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit,
360.0 / limit);
path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit,
- 360.0 / limit);
path.closeSubpath();
return path;
}
void KisPopupPalette::mouseMoveEvent(QMouseEvent* event)
{
QPointF point = event->posF();
event->accept();
QPainterPath pathBrush(drawDonutPathFull(width() / 2, height() / 2, brushInnerRadius, brushOuterRadius));
QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius));
setToolTip("");
setHoveredPreset(-1);
setHoveredColor(-1);
if (pathBrush.contains(point)) {
//in favorite brushes area
int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) {
setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name());
setHoveredPreset(pos);
}
} else if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
setHoveredColor(pos);
}
}
update();
}
void KisPopupPalette::mousePressEvent(QMouseEvent* event)
{
QPointF point = event->posF();
event->accept();
if (event->button() == Qt::LeftButton) {
QPainterPath pathBrush(drawDonutPathFull(width() / 2, height() / 2, brushInnerRadius, brushOuterRadius));
if (pathBrush.contains(point)) {
//in favorite brushes area
int pos = calculateIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()
&& isPointInPixmap(point, pos)) {
//setSelectedBrush(pos);
update();
}
} else {
int side = qMin(width(), height());
QPainterPath settingCircle;
settingCircle.addEllipse(width() / 2 + side / 2 - 40, height() / 2 + side / 2 - 40, 40, 40);
if (settingCircle.contains(point)) {
KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QStringList tags = rServer->tagNamesList();
qSort(tags);
if (!tags.isEmpty()) {
QMenu menu;
foreach(const QString& tag, tags) {
menu.addAction(tag);
}
QAction* action = menu.exec(event->globalPos());
if (action) {
m_resourceManager->setCurrentTag(action->text());
}
} else {
QWhatsThis::showText(event->globalPos(),
i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here."));
}
}
}
}
}
void KisPopupPalette::mouseReleaseEvent(QMouseEvent * event)
{
QPointF point = event->posF();
event->accept();
if (event->button() == Qt::LeftButton) {
QPainterPath pathBrush(drawDonutPathFull(width() / 2, height() / 2, brushInnerRadius, brushOuterRadius));
QPainterPath pathColor(drawDonutPathFull(width() / 2, height() / 2, colorInnerRadius, colorOuterRadius));
if (pathBrush.contains(point)) {
//in favorite brushes area
if (hoveredPreset() > -1) {
//setSelectedBrush(hoveredBrush());
emit sigChangeActivePaintop(hoveredPreset());
}
} else if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
emit sigUpdateRecentColor(pos);
}
}
} else if (event->button() == Qt::RightButton) {
showPopupPalette(false);
}
}
int KisPopupPalette::calculateIndex(QPointF point, int n)
{
calculatePresetIndex(point, n);
//translate to (0,0)
point.setX(point.x() - width() / 2);
point.setY(point.y() - height() / 2);
//rotate
float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x());
float radius = sqrt((float)point.x() * point.x() + point.y() * point.y());
point.setX(radius * cos(smallerAngle));
point.setY(radius * sin(smallerAngle));
//calculate brush index
int pos = floor(acos(point.x() / radius) * n / (2 * M_PI));
if (point.y() < 0) pos = n - pos - 1;
return pos;
}
bool KisPopupPalette::isPointInPixmap(QPointF& point, int pos)
{
if (pathFromPresetIndex(pos).contains(point + QPointF(-width() / 2, -height() / 2))) {
return true;
}
return false;
}
KisPopupPalette::~KisPopupPalette()
{
}
QPainterPath KisPopupPalette::pathFromPresetIndex(int index)
{
QRect outerRect(-width() / 2, -height() / 2, width(), height());
outerRect.adjust(3, 3, -3, -3);
int ringSize = brushOuterRadius - colorOuterRadius - 6;
QRect innerRect = outerRect.adjusted(ringSize, ringSize, -ringSize, -ringSize);
qreal angleLength = 360.0 / numSlots();
qreal angle = index * angleLength;
QPainterPath path;
path.moveTo(brushOuterRadius * cos(angle / 180 * M_PI), -brushOuterRadius * sin(angle / 180 * M_PI));
path.arcTo(outerRect, angle, angleLength);
path.arcTo(innerRect, angle + angleLength, -angleLength);
path.closeSubpath();
return path;
}
int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/)
{
int x = point.x() - width() / 2;
int y = point.y() - height() / 2;
qreal radius = sqrt((qreal) x * x + y * y);
qreal angle;
// y coordinate is the reverse of the cartesian one
if (y < 0) {
angle = acos(x / radius);
} else {
angle = 2 * M_PI - acos(x / radius);
}
int pos = floor(angle / (2 * M_PI / numSlots()));
return pos;
}
int KisPopupPalette::numSlots()
{
KisConfig config;
return qMax(config.favoritePresets(), 10);
}
diff --git a/krita/ui/kis_psd_layer_style_resource.cpp b/krita/ui/kis_psd_layer_style_resource.cpp
index bd9859d2aad..3d3964974ba 100644
--- a/krita/ui/kis_psd_layer_style_resource.cpp
+++ b/krita/ui/kis_psd_layer_style_resource.cpp
@@ -1,171 +1,171 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_psd_layer_style_resource.h"
#include <QBuffer>
#include <QString>
#include <QByteArray>
#include <QFile>
#include <QFileInfo>
#include <QCryptographicHash>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <klocale.h>
#include "kis_psd_layer_style.h"
#include "kis_asl_layer_style_serializer.h"
#include "kis_layer.h"
KisPSDLayerStyleCollectionResource::KisPSDLayerStyleCollectionResource(const QString &filename)
: KoResource(filename)
{
if (!filename.isEmpty()) {
setName(QFileInfo(filename).fileName());
}
}
KisPSDLayerStyleCollectionResource::~KisPSDLayerStyleCollectionResource()
{
m_layerStyles.clear();
}
bool KisPSDLayerStyleCollectionResource::load()
{
QFile file(filename());
if (file.size() == 0) return false;
bool result;
if (!file.open(QIODevice::ReadOnly)) {
- kWarning() << "Can't open file " << filename();
+ dbgKrita << "Can't open file " << filename();
return false;
}
result = loadFromDevice(&file);
file.close();
setName(QFileInfo(filename()).fileName());
return result;
}
bool KisPSDLayerStyleCollectionResource::loadFromDevice(QIODevice *dev)
{
KisAslLayerStyleSerializer serializer;
serializer.readFromDevice(dev);
m_layerStyles = serializer.styles();
setValid(true);
return true;
}
bool KisPSDLayerStyleCollectionResource::save()
{
QFile file(filename());
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
bool res = saveToDevice(&file);
file.close();
return res;
}
bool KisPSDLayerStyleCollectionResource::saveToDevice(QIODevice *dev) const
{
if (m_layerStyles.isEmpty()) return true;
KisAslLayerStyleSerializer serializer;
serializer.setStyles(m_layerStyles);
serializer.saveToDevice(dev);
return true;
}
QString KisPSDLayerStyleCollectionResource::defaultFileExtension() const
{
return QString(".asl");
}
KisPSDLayerStyleCollectionResource::StylesVector KisPSDLayerStyleCollectionResource::layerStyles() const
{
return m_layerStyles;
}
void KisPSDLayerStyleCollectionResource::setLayerStyles(StylesVector styles)
{
m_layerStyles = styles;
setValid(!m_layerStyles.isEmpty());
}
QByteArray KisPSDLayerStyleCollectionResource::generateMD5() const
{
if (m_layerStyles.size() > 0) {
QBuffer buf;
buf.open(QIODevice::WriteOnly);
saveToDevice(&buf);
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(buf.buffer());
return md5.result();
}
return QByteArray();
}
void KisPSDLayerStyleCollectionResource::collectAllLayerStyles(KisNodeSP root)
{
KisLayer* layer = dynamic_cast<KisLayer*>(root.data());
if (layer && layer->layerStyle()) {
KisPSDLayerStyleSP clone = layer->layerStyle()->clone();
clone->setName(i18nc("Auto-generated layer style name for embedded styles (style itself)", "<%1> (embedded)", layer->name()));
m_layerStyles << clone;
setValid(true);
}
KisNodeSP child = root->firstChild();
while (child) {
collectAllLayerStyles(child);
child = child->nextSibling();
}
}
void KisPSDLayerStyleCollectionResource::assignAllLayerStyles(KisNodeSP root)
{
KisLayer* layer = dynamic_cast<KisLayer*>(root.data());
if (layer && layer->layerStyle()) {
QUuid uuid = layer->layerStyle()->uuid();
bool found = false;
foreach (KisPSDLayerStyleSP style, m_layerStyles) {
if (style->uuid() == uuid) {
layer->setLayerStyle(style->clone());
found = true;
break;
}
}
if (!found) {
- qWarning() << "WARNING: loading layer style for" << layer->name() << "failed! It requests unexistent style:" << uuid;
+ warnKrita << "WARNING: loading layer style for" << layer->name() << "failed! It requests unexistent style:" << uuid;
}
}
KisNodeSP child = root->firstChild();
while (child) {
assignAllLayerStyles(child);
child = child->nextSibling();
}
}
diff --git a/krita/ui/kis_resource_server_provider.cpp b/krita/ui/kis_resource_server_provider.cpp
index d4c32835356..2bda9e301d9 100644
--- a/krita/ui/kis_resource_server_provider.cpp
+++ b/krita/ui/kis_resource_server_provider.cpp
@@ -1,142 +1,142 @@
/*
* kis_resourceserver.cc - part of KImageShop
*
* Copyright (c) 1999 Matthias Elter <elter@kde.org>
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_resource_server_provider.h"
#include <QDir>
#include <QApplication>
-#include <QDebug>
+#include <kis_debug.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kcomponentdata.h>
#include <KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <KoResourceServerAdapter.h>
#include <kis_debug.h>
#include <KoPattern.h>
#include <kis_paintop_preset.h>
#include <kis_workspace_resource.h>
#include <kis_psd_layer_style_resource.h>
#include <kis_brush_server.h>
typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
inline bool isRunningInKrita() {
return qApp->applicationName().contains(QLatin1String("krita"), Qt::CaseInsensitive);
}
KisResourceServerProvider::KisResourceServerProvider()
{
KisBrushServer *brushServer = KisBrushServer::instance();
KGlobal::dirs()->addResourceType("kis_paintoppresets", "data", "krita/paintoppresets/");
KGlobal::dirs()->addResourceDir("kis_paintoppresets", QDir::homePath() + QString("/.create/paintoppresets/krita"));
KGlobal::dirs()->addResourceType("kis_workspaces", "data", "krita/workspaces/");
KGlobal::dirs()->addResourceType("psd_layer_style_collections", "data", "krita/asl");
m_paintOpPresetServer = new KisPaintOpPresetResourceServer("kis_paintoppresets", "*.kpp");
if (!QFileInfo(m_paintOpPresetServer->saveLocation()).exists()) {
QDir().mkpath(m_paintOpPresetServer->saveLocation());
}
m_paintOpPresetThread = new KoResourceLoaderThread(m_paintOpPresetServer);
m_paintOpPresetThread->start();
if (!isRunningInKrita()) {
m_paintOpPresetThread->barrier();
}
m_workspaceServer = new KoResourceServerSimpleConstruction<KisWorkspaceResource>("kis_workspaces", "*.kws");
if (!QFileInfo(m_workspaceServer->saveLocation()).exists()) {
QDir().mkpath(m_workspaceServer->saveLocation());
}
m_workspaceThread = new KoResourceLoaderThread(m_workspaceServer);
m_workspaceThread->start();
if (!isRunningInKrita()) {
m_workspaceThread->barrier();
}
m_layerStyleCollectionServer = new KoResourceServerSimpleConstruction<KisPSDLayerStyleCollectionResource>("psd_layer_style_collections", "*.asl");
if (!QFileInfo(m_layerStyleCollectionServer->saveLocation()).exists()) {
QDir().mkpath(m_layerStyleCollectionServer->saveLocation());
}
m_layerStyleCollectionThread = new KoResourceLoaderThread(m_layerStyleCollectionServer);
m_layerStyleCollectionThread->start();
if (!isRunningInKrita()) {
m_layerStyleCollectionThread->barrier();
}
connect(this, SIGNAL(notifyBrushBlacklistCleanup()),
brushServer, SLOT(slotRemoveBlacklistedResources()));
}
KisResourceServerProvider::~KisResourceServerProvider()
{
delete m_paintOpPresetThread;
delete m_workspaceThread;
delete m_layerStyleCollectionThread;
delete m_paintOpPresetServer;
delete m_workspaceServer;
delete m_layerStyleCollectionServer;
}
KisResourceServerProvider* KisResourceServerProvider::instance()
{
K_GLOBAL_STATIC(KisResourceServerProvider, s_instance);
return s_instance;
}
KisPaintOpPresetResourceServer* KisResourceServerProvider::paintOpPresetServer(bool block)
{
if (block) m_paintOpPresetThread->barrier();
return m_paintOpPresetServer;
}
KoResourceServer< KisWorkspaceResource >* KisResourceServerProvider::workspaceServer(bool block)
{
if (block) m_workspaceThread->barrier();
return m_workspaceServer;
}
KoResourceServer<KisPSDLayerStyleCollectionResource> *KisResourceServerProvider::layerStyleCollectionServer(bool block)
{
if (block) m_layerStyleCollectionThread->barrier();
return m_layerStyleCollectionServer;
}
void KisResourceServerProvider::brushBlacklistCleanup()
{
emit notifyBrushBlacklistCleanup();
}
diff --git a/krita/ui/kis_safe_document_loader.cpp b/krita/ui/kis_safe_document_loader.cpp
index cfeec1f3071..4e05972ed1a 100644
--- a/krita/ui/kis_safe_document_loader.cpp
+++ b/krita/ui/kis_safe_document_loader.cpp
@@ -1,173 +1,173 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_safe_document_loader.h"
#include <QTimer>
#include <QFileSystemWatcher>
#include <QApplication>
#include <kurl.h>
#include "KisDocument.h"
#include "kis_image.h"
#include "kis_signal_compressor.h"
#include "KisPart.h"
struct KisSafeDocumentLoader::Private
{
Private()
: fileChangedSignalCompressor(500 /* ms */, KisSignalCompressor::POSTPONE),
isLoading(false),
fileChangedFlag(false)
{
}
QScopedPointer<KisDocument> doc;
QFileSystemWatcher fileWatcher;
KisSignalCompressor fileChangedSignalCompressor;
QTimer delayedLoadTimer;
bool isLoading;
bool fileChangedFlag;
QString path;
QString temporaryPath;
qint64 initialFileSize;
QDateTime initialFileTimeStamp;
};
KisSafeDocumentLoader::KisSafeDocumentLoader(const QString &path, QObject *parent)
: QObject(parent),
m_d(new Private())
{
connect(&m_d->fileWatcher, SIGNAL(fileChanged(QString)),
SLOT(fileChanged()));
connect(&m_d->fileWatcher, SIGNAL(fileChanged(QString)),
&m_d->fileChangedSignalCompressor, SLOT(start()));
connect(&m_d->fileChangedSignalCompressor, SIGNAL(timeout()),
SLOT(fileChangedCompressed()));
connect(&m_d->delayedLoadTimer, SIGNAL(timeout()),
SLOT(delayedLoadStart()));
m_d->delayedLoadTimer.setSingleShot(true);
m_d->delayedLoadTimer.setInterval(100 /* ms */);
setPath(path);
}
KisSafeDocumentLoader::~KisSafeDocumentLoader()
{
delete m_d;
}
void KisSafeDocumentLoader::setPath(const QString &path)
{
if (path.isEmpty()) return;
if (!m_d->path.isEmpty()) {
m_d->fileWatcher.removePath(m_d->path);
}
m_d->path = path;
m_d->fileWatcher.addPath(m_d->path);
}
void KisSafeDocumentLoader::reloadImage()
{
fileChangedCompressed(true);
}
void KisSafeDocumentLoader::fileChanged()
{
m_d->fileChangedFlag = true;
}
void KisSafeDocumentLoader::fileChangedCompressed(bool sync)
{
if (m_d->isLoading) return;
QFileInfo initialFileInfo(m_d->path);
m_d->initialFileSize = initialFileInfo.size();
m_d->initialFileTimeStamp = initialFileInfo.lastModified();
// it may happen when the file is flushed by
// so other application
if (!m_d->initialFileSize) return;
m_d->isLoading = true;
m_d->fileChangedFlag = false;
m_d->temporaryPath =
QDir::tempPath() + QDir::separator() +
QString("krita_file_layer_copy_%1_%2.%3")
.arg(QApplication::applicationPid())
.arg(qrand())
.arg(initialFileInfo.suffix());
QFile::copy(m_d->path, m_d->temporaryPath);
if (!sync) {
m_d->delayedLoadTimer.start();
} else {
QApplication::processEvents();
delayedLoadStart();
}
}
void KisSafeDocumentLoader::delayedLoadStart()
{
QFileInfo originalInfo(m_d->path);
QFileInfo tempInfo(m_d->temporaryPath);
bool successfullyLoaded = false;
if (!m_d->fileChangedFlag &&
originalInfo.size() == m_d->initialFileSize &&
originalInfo.lastModified() == m_d->initialFileTimeStamp &&
tempInfo.size() == m_d->initialFileSize) {
m_d->doc.reset(KisPart::instance()->createDocument());
successfullyLoaded = m_d->doc->openUrl(m_d->temporaryPath,
KisDocument::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES);
} else {
- qDebug() << "File was modified externally. Restarting.";
- qDebug() << ppVar(m_d->fileChangedFlag);
- qDebug() << ppVar(m_d->initialFileSize);
- qDebug() << ppVar(m_d->initialFileTimeStamp);
- qDebug() << ppVar(originalInfo.size());
- qDebug() << ppVar(originalInfo.lastModified());
- qDebug() << ppVar(tempInfo.size());
+ dbgKrita << "File was modified externally. Restarting.";
+ dbgKrita << ppVar(m_d->fileChangedFlag);
+ dbgKrita << ppVar(m_d->initialFileSize);
+ dbgKrita << ppVar(m_d->initialFileTimeStamp);
+ dbgKrita << ppVar(originalInfo.size());
+ dbgKrita << ppVar(originalInfo.lastModified());
+ dbgKrita << ppVar(tempInfo.size());
}
QFile::remove(m_d->temporaryPath);
m_d->isLoading = false;
if (!successfullyLoaded) {
// Restart the attempt
m_d->fileChangedSignalCompressor.start();
} else {
emit loadingFinished(m_d->doc->image());
}
m_d->doc.reset();
}
diff --git a/krita/ui/kis_selection_decoration.cc b/krita/ui/kis_selection_decoration.cc
index 54d1105f9f0..72b75733ab1 100644
--- a/krita/ui/kis_selection_decoration.cc
+++ b/krita/ui/kis_selection_decoration.cc
@@ -1,202 +1,202 @@
/*
* Copyright (c) 2008 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_selection_decoration.h"
#include <QPainter>
#include <QVarLengthArray>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <klocale.h>
#include "kis_types.h"
#include "KisViewManager.h"
#include "kis_selection.h"
#include "kis_image.h"
#include "flake/kis_shape_selection.h"
#include "kis_pixel_selection.h"
#include "kis_update_outline_job.h"
#include "kis_selection_manager.h"
#include "canvas/kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "kis_coordinates_converter.h"
#include "kis_config.h"
#include "krita_utils.h"
#include "KisView.h"
static const unsigned int ANT_LENGTH = 4;
static const unsigned int ANT_SPACE = 4;
static const unsigned int ANT_ADVANCE_WIDTH = ANT_LENGTH + ANT_SPACE;
KisSelectionDecoration::KisSelectionDecoration(QPointer<KisView>view)
: KisCanvasDecoration("selection", view),
m_signalCompressor(500 /*ms*/, KisSignalCompressor::FIRST_INACTIVE),
m_offset(0),
m_mode(Ants)
{
KritaUtils::initAntsPen(&m_antsPen, &m_outlinePen,
ANT_LENGTH, ANT_SPACE);
m_antsTimer = new QTimer(this);
m_antsTimer->setInterval(150);
m_antsTimer->setSingleShot(false);
connect(m_antsTimer, SIGNAL(timeout()), SLOT(antsAttackEvent()));
connect(&m_signalCompressor, SIGNAL(timeout()), SLOT(slotStartUpdateSelection()));
}
KisSelectionDecoration::~KisSelectionDecoration()
{
}
KisSelectionDecoration::Mode KisSelectionDecoration::mode() const
{
return m_mode;
}
void KisSelectionDecoration::setMode(Mode mode)
{
m_mode = mode;
selectionChanged();
}
bool KisSelectionDecoration::selectionIsActive()
{
KisImageWSP image = view()->image();
Q_ASSERT(image); Q_UNUSED(image);
KisSelectionSP selection = view()->selection();
return visible() && selection &&
(selection->hasPixelSelection() || selection->hasShapeSelection()) &&
selection->isVisible();
}
void KisSelectionDecoration::selectionChanged()
{
KisSelectionSP selection = view()->selection();
if (selection && selectionIsActive()) {
if ((m_mode == Ants && selection->outlineCacheValid()) ||
(m_mode == Mask && selection->thumbnailImageValid())) {
m_signalCompressor.stop();
if (m_mode == Ants) {
m_outlinePath = selection->outlineCache();
m_antsTimer->start();
} else {
m_thumbnailImage = selection->thumbnailImage();
m_thumbnailImageTransform = selection->thumbnailImageTransform();
}
if (view() && view()->canvasBase()) {
view()->canvasBase()->updateCanvas();
}
} else {
m_signalCompressor.start();
}
} else {
m_signalCompressor.stop();
m_outlinePath = QPainterPath();
m_thumbnailImage = QImage();
m_thumbnailImageTransform = QTransform();
view()->canvasBase()->updateCanvas();
m_antsTimer->stop();
}
}
void KisSelectionDecoration::slotStartUpdateSelection()
{
KisSelectionSP selection = view()->selection();
if (!selection) return;
KisConfig cfg;
QColor maskColor = cfg.selectionOverlayMaskColor();
view()->image()->addSpontaneousJob(new KisUpdateOutlineJob(selection, m_mode == Mask, maskColor));
}
void KisSelectionDecoration::antsAttackEvent()
{
KisSelectionSP selection = view()->selection();
if (!selection) return;
if (selectionIsActive()) {
m_offset = (m_offset + 1) % ANT_ADVANCE_WIDTH;
m_antsPen.setDashOffset(m_offset);
view()->canvasBase()->updateCanvas();
}
}
void KisSelectionDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, KisCanvas2 *canvas)
{
Q_UNUSED(updateRect);
Q_UNUSED(canvas);
if (!selectionIsActive()) return;
if ((m_mode == Ants && m_outlinePath.isEmpty()) ||
(m_mode == Mask && m_thumbnailImage.isNull())) return;
KisConfig cfg;
QTransform transform = converter->imageToWidgetTransform();
gc.save();
gc.setTransform(transform, false);
if (m_mode == Mask) {
gc.setRenderHints(QPainter::SmoothPixmapTransform |
QPainter::HighQualityAntialiasing, false);
gc.setTransform(m_thumbnailImageTransform, true);
gc.drawImage(QPoint(), m_thumbnailImage);
QRect r1 = m_thumbnailImageTransform.inverted().mapRect(view()->image()->bounds());
QRect r2 = m_thumbnailImage.rect();
QPainterPath p1;
p1.addRect(r1);
QPainterPath p2;
p2.addRect(r2);
QColor maskColor = cfg.selectionOverlayMaskColor();
gc.setBrush(maskColor);
gc.setPen(Qt::NoPen);
gc.drawPath(p1 - p2);
} else /* if (m_mode == Ants) */ {
gc.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing, cfg.antialiasSelectionOutline());
// render selection outline in white
gc.setPen(m_outlinePen);
gc.drawPath(m_outlinePath);
// render marching ants in black (above the white outline)
gc.setPen(m_antsPen);
gc.drawPath(m_outlinePath);
}
gc.restore();
}
void KisSelectionDecoration::setVisible(bool v)
{
KisCanvasDecoration::setVisible(v);
selectionChanged();
}
diff --git a/krita/ui/kis_splash_screen.cpp b/krita/ui/kis_splash_screen.cpp
index c7f28113f18..4b0ac9988b5 100644
--- a/krita/ui/kis_splash_screen.cpp
+++ b/krita/ui/kis_splash_screen.cpp
@@ -1,152 +1,152 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_splash_screen.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QPixmap>
#include <QCheckBox>
-#include <QDebug>
+#include <kis_debug.h>
#include <QFile>
#include <KisPart.h>
#include <KisApplication.h>
#include <klocale.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <kcomponentdata.h>
#include <kaboutdata.h>
#include <k4aboutdata.h>
#include <kicon.h>
#include <kis_factory2.h>
KisSplashScreen::KisSplashScreen(const QString &version, const QPixmap &pixmap, bool themed, QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint | f)
{
setupUi(this);
setWindowIcon(KIcon(KGlobal::mainComponent().aboutData()->programIconName()));
QString color = "#FFFFFF";
if (themed && qApp->palette().background().color().value() >100) {
color = "#000000";
}
lblSplash->setPixmap(pixmap);
bnClose->hide();
connect(bnClose, SIGNAL(clicked()), this, SLOT(close()));
chkShowAtStartup->hide();
connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool)));
KConfigGroup cfg(KisFactory::componentData().config(), "SplashScreen");
bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
chkShowAtStartup->setChecked(hideSplash);
lblLinks->setTextFormat(Qt::RichText);
lblLinks->setText(i18n("<html>"
"<head/>"
"<body>"
"<p align=\"center\"><span style=\" color:%1;\"><b>Links</b></span></p>"
"<p><a href=\"https://krita.org/support-us/donations/\"><span style=\" text-decoration: underline; color:%1;\">Support Krita</span></a></p>"
"<p><a href=\"http://krita.org/resources\"><span style=\" text-decoration: underline; color:%1;\">Getting Started</span></a></p>"
"<p><a href=\"http://userbase.kde.org/Krita/Manual\"><span style=\" text-decoration: underline; color:%1;\">Manual</span></a></p>"
"<p><a href=\"http://krita.org\"><span style=\" text-decoration: underline; color:%1;\">Krita Website</span></a></p>"
"<p><a href=\"http://forum.kde.org/viewforum.php?f=136\"><span style=\" text-decoration: underline; color:%1;\">User Community</span></a></p>"
"<p><a href=\"https://projects.kde.org/projects/calligra\"><span style=\" text-decoration: underline; color:%1;\">Source Code</span></a></p>"
"<p><a href=\"http://store.steampowered.com/app/280680/\"><span style=\" text-decoration: underline; color:%1;\">Krita on Steam</span></a></p>"
"</body>"
"</html>").arg(color));
lblVersion->setText(i18n("Version: %1", version));
lblVersion->setStyleSheet("color:" + color);
KConfigGroup cfg2(KisFactory::componentData().config(), "RecentFiles");
int i = 1;
QString recent = i18n("<html>"
"<head/>"
"<body>"
"<p align=\"center\"><b><span style=\" color:%1;\">Recent Files</span></b></p>").arg(color);
QString path;
QStringList recentfiles;
do {
path = cfg2.readPathEntry(QString("File%1").arg(i), QString());
if (!path.isEmpty()) {
QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString());
KUrl url(path);
if (name.isEmpty())
name = url.fileName();
if (!url.isLocalFile() || QFile::exists(url.toLocalFile())) {
recentfiles.insert(0, QString("<p><a href=\"%1\"><span style=\"color:%3;\">%2</span></a></p>").arg(path).arg(name).arg(color));
}
}
i++;
} while (!path.isEmpty() || i <= 8);
recent += recentfiles.join("\n");
recent += "</body>"
"</html>";
lblRecent->setText(recent);
connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString)));
}
void KisSplashScreen::repaint()
{
QWidget::repaint();
QApplication::flush();
}
void KisSplashScreen::show()
{
QRect r(QPoint(), sizeHint());
resize(r.size());
move(QApplication::desktop()->availableGeometry().center() - r.center());
if (isVisible()) {
repaint();
}
}
void KisSplashScreen::toggleShowAtStartup(bool toggle)
{
KConfigGroup cfg(KGlobal::config(), "SplashScreen");
cfg.writeEntry("HideSplashAfterStartup", toggle);
}
void KisSplashScreen::linkClicked(const QString &link)
{
KisPart::instance()->openExistingFile(KUrl(link));
if (isTopLevel()) {
close();
}
}
diff --git a/krita/ui/kis_tooltip_manager.cpp b/krita/ui/kis_tooltip_manager.cpp
index 0fc824cf414..dc57cf4161d 100644
--- a/krita/ui/kis_tooltip_manager.cpp
+++ b/krita/ui/kis_tooltip_manager.cpp
@@ -1,99 +1,99 @@
/*
* Copyright (c) 2014 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tooltip_manager.h"
#include <QFile>
#include <QAction>
#include <QInputDialog>
#include <QDomDocument>
#include <QFile>
#include <kactioncollection.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <kmenubar.h>
#include <KisMainWindow.h>
#include "KisViewManager.h"
KisTooltipManager::KisTooltipManager(KisViewManager* view) : QObject(view), m_view(view), m_recording(false)
{
m_view->mainWindow()->menuBar()->installEventFilter(this);
}
KisTooltipManager::~KisTooltipManager()
{
if (m_recording) {
QFile f("tooltips.txt");
f.open(QFile::WriteOnly);
QDomDocument doc;
QDomElement root;
root = doc.createElement("tooltips");
doc.appendChild(root);
QMapIterator<QString, QString> it(m_tooltipMap);
while (it.hasNext()) {
it.next();
QDomElement tooltip = doc.createElement("tooltip");
tooltip.setAttribute("action", it.key());
tooltip.appendChild(doc.createTextNode(it.value()));
root.appendChild(tooltip);
}
QTextStream stream(&f);
stream << doc.toString();
f.close();
}
}
void KisTooltipManager::record()
{
m_recording = true;
QList<QAction*> actions = m_view->actionCollection()->actions();
foreach(KXMLGUIClient* client, m_view->mainWindow()->childClients() ) {
actions.append(client->actionCollection()->actions());
}
foreach(QAction* action, actions) {
action->disconnect();
connect(action, SIGNAL(triggered()), this, SLOT(captureToolip()));
}
}
void KisTooltipManager::captureToolip()
{
QString id = sender()->objectName();
QString oldTooltip;
if (m_tooltipMap.contains(id)) {
oldTooltip = m_tooltipMap[id];
}
bool ok;
QString tooltip = QInputDialog::getText(m_view->mainWindow(), "Add Tooltip",
"New Tooltip:", QLineEdit::Normal,
oldTooltip, &ok);
if (ok && !tooltip.isEmpty()) {
dynamic_cast<QAction*>(sender())->setToolTip(tooltip);
m_tooltipMap[id] = tooltip;
}
}
diff --git a/krita/ui/kis_zoom_manager.cc b/krita/ui/kis_zoom_manager.cc
index 8e5e164e947..954e795f1da 100644
--- a/krita/ui/kis_zoom_manager.cc
+++ b/krita/ui/kis_zoom_manager.cc
@@ -1,302 +1,302 @@
/*
* Copyright (C) 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2009 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_zoom_manager.h"
#include <QGridLayout>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <kstatusbar.h>
#include <ktoggleaction.h>
#include <kis_debug.h>
#include <KisView.h>
#include <KoZoomAction.h>
#include <KoRuler.h>
#include <KoZoomHandler.h>
#include <KoZoomController.h>
#include <KoCanvasControllerWidget.h>
#include <KoRulerController.h>
#include <KoUnit.h>
#include <KoDpi.h>
#include "KisDocument.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "kis_image.h"
#include "kis_statusbar.h"
#include "kis_config.h"
#include "krita_utils.h"
#include "kis_canvas_resource_provider.h"
class KisZoomController : public KoZoomController
{
public:
KisZoomController(KoCanvasController *co, KisCoordinatesConverter *zh, KActionCollection *actionCollection, KoZoomAction::SpecialButtons specialButtons, QObject *parent)
: KoZoomController(co, zh, actionCollection, false, specialButtons, parent),
m_converter(zh)
{
}
protected:
QSize documentToViewport(const QSizeF &size) {
QRectF docRect(QPointF(), size);
return m_converter->documentToWidget(docRect).toRect().size();
}
private:
KisCoordinatesConverter *m_converter;
};
KisZoomManager::KisZoomManager(QPointer<KisView> view, KoZoomHandler * zoomHandler,
KoCanvasController * canvasController)
: m_view(view)
, m_zoomHandler(zoomHandler)
, m_canvasController(canvasController)
, m_horizontalRuler(0)
, m_verticalRuler(0)
, m_zoomAction(0)
, m_zoomActionWidget(0)
{
}
KisZoomManager::~KisZoomManager()
{
KisConfig cfg;
cfg.setShowRulers(m_horizontalRuler->isVisible());
if (m_zoomActionWidget && !m_zoomActionWidget->parent()) {
delete m_zoomActionWidget;
}
}
void KisZoomManager::setup(KActionCollection * actionCollection)
{
KisImageWSP image = m_view->image();
if (!image) return;
connect(image, SIGNAL(sigSizeChanged(const QPointF &, const QPointF &)), this, SLOT(setMinMaxZoom()));
KisCoordinatesConverter *converter =
dynamic_cast<KisCoordinatesConverter*>(m_zoomHandler);
m_zoomController = new KisZoomController(m_canvasController, converter, actionCollection, KoZoomAction::AspectMode, this);
m_zoomHandler->setZoomMode(KoZoomMode::ZOOM_PIXELS);
m_zoomHandler->setZoom(1.0);
m_zoomController->setPageSize(QSizeF(image->width() / image->xRes(), image->height() / image->yRes()));
m_zoomController->setDocumentSize(QSizeF(image->width() / image->xRes(), image->height() / image->yRes()), true);
m_zoomAction = m_zoomController->zoomAction();
setMinMaxZoom();
m_zoomActionWidget = m_zoomAction->createWidget(0);
// Put the canvascontroller in a layout so it resizes with us
QGridLayout * layout = new QGridLayout(m_view);
layout->setSpacing(0);
layout->setMargin(0);
m_view->setLayout(layout);
m_view->document()->setUnit(KoUnit(KoUnit::Pixel));
m_horizontalRuler = new KoRuler(m_view, Qt::Horizontal, m_zoomHandler);
m_horizontalRuler->setShowMousePosition(true);
m_horizontalRuler->createGuideToolConnection(m_view->canvasBase());
new KoRulerController(m_horizontalRuler, m_canvasController->canvas()->resourceManager());
m_verticalRuler = new KoRuler(m_view, Qt::Vertical, m_zoomHandler);
m_verticalRuler->setShowMousePosition(true);
m_verticalRuler->createGuideToolConnection(m_view->canvasBase());
QList<QAction*> unitActions = m_view->createChangeUnitActions(true);
m_horizontalRuler->setPopupActionList(unitActions);
m_verticalRuler->setPopupActionList(unitActions);
connect(m_view->document(), SIGNAL(unitChanged(const KoUnit&)), SLOT(applyRulersUnit(const KoUnit&)));
layout->addWidget(m_horizontalRuler, 0, 1);
layout->addWidget(m_verticalRuler, 1, 0);
layout->addWidget(static_cast<KoCanvasControllerWidget*>(m_canvasController), 1, 1);
connect(m_canvasController->proxyObject, SIGNAL(canvasOffsetXChanged(int)),
this, SLOT(pageOffsetChanged()));
connect(m_canvasController->proxyObject, SIGNAL(canvasOffsetYChanged(int)),
this, SLOT(pageOffsetChanged()));
connect(m_canvasController->proxyObject,
SIGNAL(canvasMousePositionChanged(const QPoint &)),
SLOT(mousePositionChanged(const QPoint &)));
connect(m_zoomController, SIGNAL(zoomChanged(KoZoomMode::Mode, qreal)),
this, SLOT(slotZoomChanged(KoZoomMode::Mode, qreal)));
connect(m_zoomController, SIGNAL(aspectModeChanged(bool)),
this, SLOT(changeAspectMode(bool)));
applyRulersUnit(m_view->document()->unit());
KisConfig cfg;
toggleShowRulers(cfg.showRulers());
}
void KisZoomManager::mousePositionChanged(const QPoint &viewPos)
{
QPoint pt = viewPos - m_rulersOffset;
m_horizontalRuler->updateMouseCoordinate(pt.x());
m_verticalRuler->updateMouseCoordinate(pt.y());
}
bool KisZoomManager::horizontalRulerVisible() const
{
return m_horizontalRuler->isVisible();
}
bool KisZoomManager::verticalRulerVisible() const
{
return m_verticalRuler->isVisible();
}
void KisZoomManager::toggleShowRulers(bool show)
{
m_horizontalRuler->setVisible(show);
m_verticalRuler->setVisible(show);
}
void KisZoomManager::applyRulersUnit(const KoUnit &baseUnit)
{
m_horizontalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->xRes()));
m_verticalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->yRes()));
}
void KisZoomManager::setMinMaxZoom()
{
KisImageWSP image = m_view->image();
if (!image) return;
QSize imageSize = image->size();
qreal minDimension = qMin(imageSize.width(), imageSize.height());
qreal minZoom = qMin(100.0 / minDimension, 0.1);
m_zoomAction->setMinimumZoom(minZoom);
m_zoomAction->setMaximumZoom(90.0);
}
void KisZoomManager::updateGUI()
{
QRectF widgetRect = m_view->canvasBase()->coordinatesConverter()->imageRectInWidgetPixels();
QSize documentSize = m_view->canvasBase()->viewConverter()->viewToDocument(widgetRect).toAlignedRect().size();
m_horizontalRuler->setRulerLength(documentSize.width());
m_verticalRuler->setRulerLength(documentSize.height());
applyRulersUnit(m_horizontalRuler->unit());
}
QWidget *KisZoomManager::zoomActionWidget() const
{
return m_zoomActionWidget;
}
void KisZoomManager::slotZoomChanged(KoZoomMode::Mode mode, qreal zoom)
{
Q_UNUSED(mode);
Q_UNUSED(zoom);
m_view->canvasBase()->notifyZoomChanged();
qreal humanZoom = zoom * 100.0;
// XXX: KOMVC -- this is very irritating in MDI mode
if (m_view->viewManager()) {
m_view->viewManager()->
showFloatingMessage(
i18nc("floating message about zoom", "Zoom: %1 %",
KritaUtils::prettyFormatReal(humanZoom)),
QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter);
}
qreal scaleX, scaleY;
m_view->canvasBase()->coordinatesConverter()->imageScale(&scaleX, &scaleY);
if (scaleX != scaleY) {
- qWarning() << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY));
+ warnKrita << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY));
}
// zoom by average of x and y
const qreal effectiveZoom = 0.5 * (scaleX + scaleY);
m_view->canvasBase()->resourceManager()->setResource(KisCanvasResourceProvider::EffectiveZoom, effectiveZoom);
}
void KisZoomManager::slotScrollAreaSizeChanged()
{
pageOffsetChanged();
updateGUI();
}
void KisZoomManager::changeAspectMode(bool aspectMode)
{
KisImageWSP image = m_view->image();
KoZoomMode::Mode newMode = KoZoomMode::ZOOM_CONSTANT;
qreal newZoom = m_zoomHandler->zoom();
qreal resolutionX = aspectMode ? image->xRes() : POINT_TO_INCH(static_cast<qreal>(KoDpi::dpiX()));
qreal resolutionY = aspectMode ? image->yRes() : POINT_TO_INCH(static_cast<qreal>(KoDpi::dpiY()));
m_zoomController->setZoom(newMode, newZoom, resolutionX, resolutionY);
m_view->canvasBase()->notifyZoomChanged();
}
void KisZoomManager::pageOffsetChanged()
{
QRectF widgetRect = m_view->canvasBase()->coordinatesConverter()->imageRectInWidgetPixels();
m_rulersOffset = widgetRect.topLeft().toPoint();
m_horizontalRuler->setOffset(m_rulersOffset.x());
m_verticalRuler->setOffset(m_rulersOffset.y());
}
void KisZoomManager::zoomTo100()
{
m_zoomController->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
m_view->canvasBase()->notifyZoomChanged();
}
void KisZoomManager::showGuides(bool toggle)
{
m_view->document()->guidesData().setShowGuideLines(toggle);
m_view->canvasBase()->canvasWidget()->update();
}
diff --git a/krita/ui/kisexiv2/kis_exif_io.cpp b/krita/ui/kisexiv2/kis_exif_io.cpp
index bfa39317153..a6103aa575e 100644
--- a/krita/ui/kisexiv2/kis_exif_io.cpp
+++ b/krita/ui/kisexiv2/kis_exif_io.cpp
@@ -1,632 +1,632 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_exif_io.h"
#include <exiv2/exif.hpp>
#include <exiv2/error.hpp>
#include <qendian.h>
#include <QIODevice>
#include <QByteArray>
#include <QVariant>
#include <QDateTime>
#include <QDate>
#include <QTime>
#include <QTextCodec>
#include <kis_debug.h>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
#include <filters/libmsooxml/MsooXmlDiagramReader_p.h>
struct KisExifIO::Private {
};
// ---- Exception convertion functions ---- //
// convert ExifVersion and FlashpixVersion to a KisMetaData value
KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
{
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if(dvalue)
{
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
return KisMetaData::Value(QString(array));
} else {
Q_ASSERT(value->typeId() == Exiv2::asciiString);
return KisMetaData::Value(QString::fromLatin1(value->toString().c_str()));
}
}
// convert from KisMetaData value to ExifVersion and FlashpixVersion
Exiv2::Value* kmdValueToExifVersion(const KisMetaData::Value& value)
{
Exiv2::DataValue* dvalue = new Exiv2::DataValue;
QString ver = value.asVariant().toString();
dvalue->read((const Exiv2::byte*)ver.toLatin1().constData(), ver.size());
return dvalue;
}
// Convert an exif array of integer string to a KisMetaData array of integer
KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
{
QList<KisMetaData::Value> v;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if(dvalue)
{
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
for (int i = 0; i < array.size(); i++) {
QChar c((char)array[i]);
v.push_back(KisMetaData::Value(QString(c).toInt(0)));
}
} else {
Q_ASSERT(value->typeId() == Exiv2::asciiString);
QString str = QString::fromLatin1(value->toString().c_str());
v.push_back(KisMetaData::Value(str.toInt()));
}
return KisMetaData::Value(v, KisMetaData::Value::OrderedArray);
}
// Convert a KisMetaData array of integer to an exif array of integer string
Exiv2::Value* kmdIntOrderedArrayToExifArray(const KisMetaData::Value& value)
{
QList<KisMetaData::Value> v = value.asArray();
QByteArray s;
for (QList<KisMetaData::Value>::iterator it = v.begin();
it != v.end(); ++it) {
int val = it->asVariant().toInt(0);
s += QByteArray::number(val);
}
return new Exiv2::DataValue((const Exiv2::byte*)s.data(), s.size());
}
QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
{
return QDateTime::fromString(value->toString().c_str(), Qt::ISODate);
}
template<typename T>
inline T fixEndianess(T v, Exiv2::ByteOrder order)
{
switch (order) {
case Exiv2::invalidByteOrder:
return v;
case Exiv2::littleEndian:
return qFromLittleEndian<T>(v);
case Exiv2::bigEndian:
return qFromBigEndian<T>(v);
}
warnKrita << "KisExifIO: unknown byte order";
return v;
}
Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
{
switch (order) {
case Exiv2::invalidByteOrder:
warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder";
case Exiv2::littleEndian:
return Exiv2::bigEndian;
case Exiv2::bigEndian:
return Exiv2::littleEndian;
}
return Exiv2::invalidByteOrder;
}
KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
QMap<QString, KisMetaData::Value> oecfStructure;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
if ((columns * rows + 4) > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
order = invertByteOrder(order);
columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
Q_ASSERT((columns * rows + 4) > dvalue->count());
}
oecfStructure["Columns"] = KisMetaData::Value(columns);
oecfStructure["Rows"] = KisMetaData::Value(rows);
int index = 4;
QList<KisMetaData::Value> names;
for (int i = 0; i < columns; i++) {
int lastIndex = array.indexOf((char)0, index);
QString name = array.mid(index, lastIndex - index);
if (index != lastIndex) {
index = lastIndex + 1;
dbgFile << "Name [" << i << "] =" << name;
names.append(KisMetaData::Value(name));
} else {
names.append(KisMetaData::Value(""));
}
}
oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray);
QList<KisMetaData::Value> values;
qint16* dataIt = reinterpret_cast<qint16*>(array.data() + index);
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
values.append(KisMetaData::Value(KisMetaData::Rational(fixEndianess<qint32>(dataIt[0], order), fixEndianess<qint32>(dataIt[1], order))));
dataIt += 8;
}
}
oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
dbgFile << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count());
return KisMetaData::Value(oecfStructure);
}
Exiv2::Value* kmdOECFStructureToExifOECF(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> oecfStructure = value.asStructure();
quint16 columns = oecfStructure["Columns"].asVariant().toInt(0);
quint16 rows = oecfStructure["Rows"].asVariant().toInt(0);
QList<KisMetaData::Value> names = oecfStructure["Names"].asArray();
QList<KisMetaData::Value> values = oecfStructure["Values"].asArray();
Q_ASSERT(columns*rows == values.size());
int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational)
bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0);
if (saveNames) {
for (int i = 0; i < columns; i++) {
length += names[i].asVariant().toString().size() + 1;
}
}
QByteArray array(length, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
int index = 4;
if (saveNames) {
for (int i = 0; i < columns; i++) {
QByteArray name = names[i].asVariant().toString().toLatin1();
name.append((char)0);
memcpy(array.data() + index, name.data(), name.size());
index += name.size();
}
}
qint16* dataIt = reinterpret_cast<qint16*>(array.data() + index);
for (QList<KisMetaData::Value>::iterator it = values.begin();
it != values.end(); ++it) {
dataIt[0] = it->asRational().numerator;
dataIt[1] = it->asRational().denominator;
dataIt += 2;
}
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
{
QMap<QString, KisMetaData::Value> deviceSettingStructure;
QByteArray array;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if(dvalue)
{
array.resize(dvalue->count());
dvalue->copy((Exiv2::byte*)array.data());
} else {
Q_ASSERT(value->typeId() == Exiv2::unsignedShort);
array.resize(2 * value->count());
value->copy((Exiv2::byte*)array.data(), Exiv2::littleEndian);
}
int columns = (reinterpret_cast<quint16*>(array.data()))[0];
int rows = (reinterpret_cast<quint16*>(array.data()))[1];
deviceSettingStructure["Columns"] = KisMetaData::Value(columns);
deviceSettingStructure["Rows"] = KisMetaData::Value(rows);
QList<KisMetaData::Value> settings;
QByteArray null(2, 0);
for (int index = 4; index < array.size(); )
{
int lastIndex = array.indexOf(null, index);
QString setting = QString::fromUtf16((ushort*)(void*)( array.data() + index), lastIndex - index + 2);
index = lastIndex + 2;
dbgFile << "Setting << " << setting;
settings.append(KisMetaData::Value(setting));
}
deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray);
return KisMetaData::Value(deviceSettingStructure);
}
Exiv2::Value* deviceSettingDescriptionKMDToExif(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> deviceSettingStructure = value.asStructure();
quint16 columns = deviceSettingStructure["Columns"].asVariant().toInt(0);
quint16 rows = deviceSettingStructure["Rows"].asVariant().toInt(0);
QTextCodec* codec = QTextCodec::codecForName("UTF-16");
QList<KisMetaData::Value> settings = deviceSettingStructure["Settings"].asArray();
QByteArray array(4, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
for (int i = 0; i < settings.count(); i++) {
QString str = settings[i].asVariant().toString();
QByteArray setting = codec->fromUnicode(str);
array.append(setting);
}
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
QMap<QString, KisMetaData::Value> cfaPatternStructure;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
if ((columns * rows + 4) != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
order = invertByteOrder(order);
columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
Q_ASSERT((columns * rows + 4) == dvalue->count());
}
cfaPatternStructure["Columns"] = KisMetaData::Value(columns);
cfaPatternStructure["Rows"] = KisMetaData::Value(rows);
QList<KisMetaData::Value> values;
int index = 4;
for (int i = 0; i < columns * rows; i++) {
values.append(KisMetaData::Value(*(array.data() + index)));
index++;
}
cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
dbgFile << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size() << ppVar(dvalue->count()));
return KisMetaData::Value(cfaPatternStructure);
}
Exiv2::Value* cfaPatternKMDToExif(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> cfaStructure = value.asStructure();
quint16 columns = cfaStructure["Columns"].asVariant().toInt(0);
quint16 rows = cfaStructure["Rows"].asVariant().toInt(0);
QList<KisMetaData::Value> values = cfaStructure["Values"].asArray();
Q_ASSERT(columns*rows == values.size());
QByteArray array(4 + columns*rows, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
for (int i = 0; i < columns * rows; i++) {
int val = values[i].asVariant().toInt();
*(array.data() + 4 + i) = val;
}
dbgFile << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size());
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
// Read and write Flash //
KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
{
uint16_t v = value->toLong();
QMap<QString, KisMetaData::Value> flashStructure;
bool fired = (v & 0x01); // bit 1 is whether flash was fired or not
flashStructure["Fired"] = QVariant(fired);
int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return
flashStructure["Return"] = QVariant(ret);
int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode
flashStructure["Mode"] = QVariant(mode);
bool function = ((v >> 5) & 0x01); // bit 6 if function
flashStructure["Function"] = QVariant(function);
bool redEye = ((v >> 6) & 0x01); // bit 7 if function
flashStructure["RedEyeMode"] = QVariant(redEye);
return KisMetaData::Value(flashStructure);
}
Exiv2::Value* flashKMDToExif(const KisMetaData::Value& value)
{
uint16_t v = 0;
QMap<QString, KisMetaData::Value> flashStructure = value.asStructure();
v = flashStructure["Fired"].asVariant().toBool();
v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1);
v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3);
v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5);
v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6);
return new Exiv2::ValueType<uint16_t>(v);
}
// ---- Implementation of KisExifIO ----//
KisExifIO::KisExifIO() : d(new Private)
{
}
KisExifIO::~KisExifIO()
{
delete d;
}
bool KisExifIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
ioDevice->open(QIODevice::WriteOnly);
Exiv2::ExifData exifData;
if (headerType == KisMetaData::IOBackend::JpegHeader) {
QByteArray header(6, 0);
header[0] = 0x45;
header[1] = 0x78;
header[2] = 0x69;
header[3] = 0x66;
header[4] = 0x00;
header[5] = 0x00;
ioDevice->write(header);
}
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
try {
const KisMetaData::Entry& entry = *it;
dbgFile << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":" << entry.schema()->uri();
QString exivKey;
if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) {
exivKey = "Exif.Image." + entry.name();
} else if (entry.schema()->uri() == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps
if (entry.name().left(3) == "GPS") {
exivKey = "Exif.GPS." + entry.name();
} else {
exivKey = "Exif.Photo." + entry.name();
}
} else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) {
if (entry.name() == "description") {
exivKey = "Exif.Image.ImageDescription";
} else if (entry.name() == "creator") {
exivKey = "Exif.Image.Artist";
} else if (entry.name() == "rights") {
exivKey = "Exif.Image.Copyright";
}
} else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) {
if (entry.name() == "ModifyDate") {
exivKey = "Exif.Image.DateTime";
} else if (entry.name() == "CreatorTool") {
exivKey = "Exif.Image.Software";
}
} else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) {
if (entry.name() == "RawData") {
exivKey = "Exif.Photo.MakerNote";
}
}
dbgFile << "Saving " << entry.name() << " to " << exivKey;
if (exivKey.isEmpty()) {
dbgFile << entry.qualifiedName() << " is unsavable to EXIF";
} else {
Exiv2::ExifKey exifKey(qPrintable(exivKey));
Exiv2::Value* v = 0;
if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") {
v = kmdValueToExifVersion(entry.value());
} else if (exivKey == "Exif.Photo.FileSource") {
char s[] = { 0x03 };
v = new Exiv2::DataValue((const Exiv2::byte*)s, 1);
} else if (exivKey == "Exif.Photo.SceneType") {
char s[] = { 0x01 };
v = new Exiv2::DataValue((const Exiv2::byte*)s, 1);
} else if (exivKey == "Exif.Photo.ComponentsConfiguration") {
v = kmdIntOrderedArrayToExifArray(entry.value());
} else if (exivKey == "Exif.Image.Artist") { // load as dc:creator
KisMetaData::Value creator = entry.value().asArray()[0];
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(creator, exifKey.defaultTypeId());
#endif
} else if (exivKey == "Exif.Photo.OECF") {
v = kmdOECFStructureToExifOECF(entry.value());
} else if (exivKey == "Exif.Photo.DeviceSettingDescription") {
v = deviceSettingDescriptionKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.CFAPattern") {
v = cfaPatternKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.Flash") {
v = flashKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.UserComment") {
Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray);
QMap<QString, KisMetaData::Value> langArr = entry.value().asLangArray();
if (langArr.contains("x-default")) {
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
v = kmdValueToExivValue(langArr.value("x-default"), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId());
#endif
} else if (langArr.size() > 0) {
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
v = kmdValueToExivValue(langArr.begin().value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId());
#endif
}
} else {
dbgFile << exifKey.tag();
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId());
#endif
}
if (v && v->typeId() != Exiv2::invalidTypeId) {
dbgFile << "Saving key" << exivKey << " of KMD value" << entry.value();
exifData.add(exifKey, v);
} else {
dbgFile << "No exif value was created for" << entry.qualifiedName() << " as" << exivKey;// << " of KMD value" << entry.value();
}
}
} catch (Exiv2::AnyError& e) {
dbgFile << "exiv error " << e.what();
}
}
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
Exiv2::DataBuf rawData = exifData.copy();
ioDevice->write((const char*) rawData.pData_, rawData.size_);
#else
Exiv2::Blob rawData;
Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData);
ioDevice->write((const char*) &*rawData.begin(), rawData.size());
#endif
ioDevice->close();
return true;
}
bool KisExifIO::canSaveAllEntries(KisMetaData::Store* /*store*/) const
{
return false; // It's a known fact that exif can't save all information, but TODO: write the check
}
bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
ioDevice->open(QIODevice::ReadOnly);
if (!ioDevice->isOpen()) {
return false;
}
QByteArray arr = ioDevice->readAll();
Exiv2::ExifData exifData;
Exiv2::ByteOrder byteOrder;
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
exifData.load((const Exiv2::byte*)arr.data(), arr.size());
byteOrder = exifData.byteOrder();
#else
try {
byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size());
}
catch (const std::exception& ex) {
- qWarning() << "Received exception trying to parse exiv data" << ex.what();
+ warnKrita << "Received exception trying to parse exiv data" << ex.what();
return false;
}
catch (...) {
- qDebug() << "Received unknown exception trying to parse exiv data";
+ dbgKrita << "Received unknown exception trying to parse exiv data";
return false;
}
#endif
dbgFile << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
dbgFile << "There are" << exifData.count() << " entries in the exif section";
const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
Q_ASSERT(tiffSchema);
const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
Q_ASSERT(exifSchema);
const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
Q_ASSERT(dcSchema);
const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
Q_ASSERT(xmpSchema);
for (Exiv2::ExifMetadata::const_iterator it = exifData.begin();
it != exifData.end(); ++it) {
if (it->key() == "Exif.Photo.StripOffsets"
|| it->key() == "RowsPerStrip"
|| it->key() == "StripByteCounts"
|| it->key() == "JPEGInterchangeFormat"
|| it->key() == "JPEGInterchangeFormatLength"
|| it->tagName() == "0x0000" ) {
dbgFile << it->key().c_str() << " is ignored";
} else if (it->key() == "Exif.Photo.MakerNote") {
const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate
store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue()))));
} else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description"
store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool"
store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.Artist") { // load as dc:creator
QList<KisMetaData::Value> creators;
creators.push_back(exivValueToKMDValue(it->getValue(), false));
store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray)));
} else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights
store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false)));
} else if (it->groupName() == "Image") {
// Tiff tags
QString fixedTN = it->tagName().c_str();
if (it->key() == "Exif.Image.ExifTag") {
dbgFile << "Ignoring " << it->key().c_str();
} else if (KisMetaData::Entry::isValidName(fixedTN)) {
store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ;
} else {
dbgFile << "Invalid tag name: " << fixedTN;
}
} else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) {
// Exif tags (and GPS tags)
KisMetaData::Value v;
if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") {
v = exifVersionToKMDValue(it->getValue());
} else if (it->key() == "Exif.Photo.FileSource") {
v = KisMetaData::Value(3);
} else if (it->key() == "Exif.Photo.SceneType") {
v = KisMetaData::Value(1);
} else if (it->key() == "Exif.Photo.ComponentsConfiguration") {
v = exifArrayToKMDIntOrderedArray(it->getValue());
} else if (it->key() == "Exif.Photo.OECF") {
v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder);
} else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") {
v = KisMetaData::Value(exivValueToDateTime(it->getValue()));
} else if (it->key() == "Exif.Photo.DeviceSettingDescription") {
v = deviceSettingDescriptionExifToKMD(it->getValue());
} else if (it->key() == "Exif.Photo.CFAPattern") {
v = cfaPatternExifToKMD(it->getValue(), byteOrder);
} else if (it->key() == "Exif.Photo.Flash") {
v = flashExifToKMD(it->getValue());
} else if (it->key() == "Exif.Photo.UserComment") {
KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false);
Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
QVariant commentVar = vUC.asVariant();
QString comment;
if (commentVar.type() == QVariant::String) {
comment = commentVar.toString();
} else if (commentVar.type() == QVariant::ByteArray) {
const QByteArray commentString = commentVar.toByteArray();
comment = QString::fromLatin1(commentString.constData(), commentString.size());
} else {
warnKrita << "KisExifIO: Unhandled UserComment value type.";
}
KisMetaData::Value vcomment(comment);
vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
QList<KisMetaData::Value> alt;
alt.append(vcomment);
v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
} else {
bool forceSeq = false;
KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
if (it->key() == "Exif.Photo.ISOSpeedRatings") {
forceSeq = true;
arrayType = KisMetaData::Value::OrderedArray;
}
v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType);
}
if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag
dbgFile << "Ignoring " << it->key().c_str();
} else {
store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v));
}
} else if (it->groupName() == "Thumbnail") {
dbgFile << "Ignoring thumbnail tag :" << it->key().c_str();
} else {
dbgFile << "Unknown exif tag, cannot load:" << it->key().c_str();
}
}
ioDevice->close();
return true;
}
diff --git a/krita/ui/kisexiv2/kis_iptc_io.cpp b/krita/ui/kisexiv2/kis_iptc_io.cpp
index 8069fae6820..e3757a48553 100644
--- a/krita/ui/kisexiv2/kis_iptc_io.cpp
+++ b/krita/ui/kisexiv2/kis_iptc_io.cpp
@@ -1,200 +1,200 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_iptc_io.h"
#include <kis_debug.h>
#include <exiv2/iptc.hpp>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
const char photoshopMarker[] = "Photoshop 3.0\0";
const char photoshopBimId_[] = "8BIM";
const uint16_t photoshopIptc = 0x0404;
const QByteArray photoshopIptc_((char*)&photoshopIptc, 2);
struct IPTCToKMD {
QString exivTag;
QString namespaceUri;
QString name;
};
static const IPTCToKMD mappings[] = {
{ "Iptc.Application2.City", KisMetaData::Schema::PhotoshopSchemaUri, "City" },
{ "Iptc.Application2.Copyright", KisMetaData::Schema::DublinCoreSchemaUri, "rights" },
{ "Iptc.Application2.CountryName", KisMetaData::Schema::PhotoshopSchemaUri, "Country" },
{ "Iptc.Application2.CountryCode", KisMetaData::Schema::IPTCSchemaUri, "CountryCode" },
{ "Iptc.Application2.Byline", KisMetaData::Schema::DublinCoreSchemaUri, "Creator" },
{ "Iptc.Application2.BylineTitle", KisMetaData::Schema::PhotoshopSchemaUri, "AuthorsPosition" },
{ "Iptc.Application2.DateCreated", KisMetaData::Schema::PhotoshopSchemaUri, "DateCreated" },
{ "Iptc.Application2.Caption", KisMetaData::Schema::DublinCoreSchemaUri, "description" },
{ "Iptc.Application2.Writer", KisMetaData::Schema::PhotoshopSchemaUri, "CaptionWriter" },
{ "Iptc.Application2.Headline", KisMetaData::Schema::PhotoshopSchemaUri, "Headline" },
{ "Iptc.Application2.SpecialInstructions", KisMetaData::Schema::PhotoshopSchemaUri, "Instructions" },
{ "Iptc.Application2.ObjectAttribute", KisMetaData::Schema::IPTCSchemaUri, "IntellectualGenre" },
{ "Iptc.Application2.TransmissionReference", KisMetaData::Schema::PhotoshopSchemaUri, "JobID" },
{ "Iptc.Application2.Keywords", KisMetaData::Schema::DublinCoreSchemaUri, "subject" },
{ "Iptc.Application2.SubLocation", KisMetaData::Schema::IPTCSchemaUri, "Location" },
{ "Iptc.Application2.Credit", KisMetaData::Schema::PhotoshopSchemaUri, "Credit" },
{ "Iptc.Application2.ProvinceState", KisMetaData::Schema::PhotoshopSchemaUri, "State" },
{ "Iptc.Application2.Source", KisMetaData::Schema::PhotoshopSchemaUri, "Source" },
{ "Iptc.Application2.Subject", KisMetaData::Schema::IPTCSchemaUri, "SubjectCode" },
{ "Iptc.Application2.ObjectName", KisMetaData::Schema::DublinCoreSchemaUri, "Title" },
{ "Iptc.Application2.Urgency", KisMetaData::Schema::PhotoshopSchemaUri, "Urgency" },
{ "Iptc.Application2.Category", KisMetaData::Schema::PhotoshopSchemaUri, "Category" },
{ "Iptc.Application2.SuppCategory", KisMetaData::Schema::PhotoshopSchemaUri, "SupplementalCategory" },
{ "", "", "" } // indicates the end of the array
};
struct KisIptcIO::Private {
QHash<QString, IPTCToKMD> iptcToKMD;
QHash<QString, IPTCToKMD> kmdToIPTC;
};
// ---- Implementation of KisExifIO ----//
KisIptcIO::KisIptcIO() : d(new Private)
{
}
KisIptcIO::~KisIptcIO()
{
delete d;
}
void KisIptcIO::initMappingsTable() const
{
// For some reason, initializing the tables in the constructor makes the it crash
if (d->iptcToKMD.size() == 0) {
for (int i = 0; !mappings[i].exivTag.isEmpty(); i++) {
dbgKrita << "mapping[i] = " << mappings[i].exivTag << " " << mappings[i].namespaceUri << " " << mappings[i].name;
d->iptcToKMD[mappings[i].exivTag] = mappings[i];
d->kmdToIPTC[
KisMetaData::SchemaRegistry::instance()
->schemaFromUri(mappings[i].namespaceUri)
->generateQualifiedName(mappings[i].name)] = mappings[i];
}
}
}
bool KisIptcIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
QStringList blockedEntries = QStringList() << "photoshop:DateCreated";
initMappingsTable();
ioDevice->open(QIODevice::WriteOnly);
Exiv2::IptcData iptcData;
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
const KisMetaData::Entry& entry = *it;
if (d->kmdToIPTC.contains(entry.qualifiedName())) {
if (blockedEntries.contains(entry.qualifiedName())) {
- qWarning() << "skipping" << entry.qualifiedName() << entry.value();
+ warnKrita << "skipping" << entry.qualifiedName() << entry.value();
continue;
}
try {
QString iptcKeyStr = d->kmdToIPTC[ entry.qualifiedName()].exivTag;
Exiv2::IptcKey iptcKey(qPrintable(iptcKeyStr));
Exiv2::Value *v = kmdValueToExivValue(entry.value(),
Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()));
if (v && v->typeId() != Exiv2::invalidTypeId) {
iptcData.add(iptcKey, v);
}
} catch (Exiv2::AnyError& e) {
dbgFile << "exiv error " << e.what();
}
}
}
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
Exiv2::DataBuf rawData = iptcData.copy();
#else
Exiv2::DataBuf rawData = Exiv2::IptcParser::encode(iptcData);
#endif
if (headerType == KisMetaData::IOBackend::JpegHeader) {
QByteArray header;
header.append(photoshopMarker);
header.append(QByteArray(1, 0)); // Null terminated string
header.append(photoshopBimId_);
header.append(photoshopIptc_);
header.append(QByteArray(2, 0));
qint32 size = rawData.size_;
QByteArray sizeArray(4, 0);
sizeArray[0] = (char)((size & 0xff000000) >> 24);
sizeArray[1] = (char)((size & 0x00ff0000) >> 16);
sizeArray[2] = (char)((size & 0x0000ff00) >> 8);
sizeArray[3] = (char)(size & 0x000000ff);
header.append(sizeArray);
ioDevice->write(header);
}
ioDevice->write((const char*) rawData.pData_, rawData.size_);
ioDevice->close();
return true;
}
bool KisIptcIO::canSaveAllEntries(KisMetaData::Store* store) const
{
Q_UNUSED(store);
return false;
}
bool KisIptcIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
initMappingsTable();
dbgFile << "Loading IPTC Tags";
ioDevice->open(QIODevice::ReadOnly);
QByteArray arr = ioDevice->readAll();
Exiv2::IptcData iptcData;
#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
iptcData.load((const Exiv2::byte*)arr.data(), arr.size());
#else
Exiv2::IptcParser::decode(iptcData, (const Exiv2::byte*)arr.data(), arr.size());
#endif
dbgFile << "There are" << iptcData.count() << " entries in the IPTC section";
for (Exiv2::IptcMetadata::const_iterator it = iptcData.begin();
it != iptcData.end(); ++it) {
dbgFile << "Reading info for key" << it->key().c_str();
if (d->iptcToKMD.contains(it->key().c_str())) {
const IPTCToKMD& iptcToKMd = d->iptcToKMD[it->key().c_str()];
const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(iptcToKMd.namespaceUri);
KisMetaData::Value value;
if (iptcToKMd.exivTag == "Iptc.Application2.Keywords") {
Q_ASSERT(it->getValue()->typeId() == Exiv2::string);
QString data = it->getValue()->toString().c_str();
QStringList list = data.split(',');
QList<KisMetaData::Value> values;
foreach(const QString &entry, list) {
values.push_back(KisMetaData::Value(entry));
}
value = KisMetaData::Value(values, KisMetaData::Value::UnorderedArray);
} else {
value = exivValueToKMDValue(it->getValue(), false);
}
store->addEntry(KisMetaData::Entry(schema, iptcToKMd.name, value));
}
}
return false;
}
diff --git a/krita/ui/kisexiv2/kis_xmp_io.cpp b/krita/ui/kisexiv2/kis_xmp_io.cpp
index 72e3d57d2a7..cf6c6c14503 100644
--- a/krita/ui/kisexiv2/kis_xmp_io.cpp
+++ b/krita/ui/kisexiv2/kis_xmp_io.cpp
@@ -1,401 +1,401 @@
/*
* Copyright (c) 2008-2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_xmp_io.h"
#include <string>
#include <exiv2/xmp.hpp>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_parser.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
#include <kis_meta_data_type_info.h>
#include <kis_debug.h>
KisXMPIO::KisXMPIO()
{
}
KisXMPIO::~KisXMPIO()
{
}
inline std::string exiv2Prefix(const KisMetaData::Schema* _schema)
{
const QByteArray latin1SchemaUri = _schema->uri().toLatin1();
std::string prefix = Exiv2::XmpProperties::prefix(latin1SchemaUri.constData());
if (prefix.empty()) {
dbgFile << "Unknown namespace " << ppVar(_schema->uri()) << ppVar(_schema->prefix());
prefix = _schema->prefix().toLatin1().constData();
Exiv2::XmpProperties::registerNs(latin1SchemaUri.constData(), prefix);
}
return prefix;
}
namespace
{
void saveStructure(Exiv2::XmpData& xmpData_, const QString& name, const std::string& prefix, const QMap<QString, KisMetaData::Value>& structure, const KisMetaData::Schema* structureSchema)
{
std::string structPrefix = exiv2Prefix(structureSchema);
for (QMap<QString, KisMetaData::Value>::const_iterator it = structure.begin();
it != structure.end(); ++it) {
Q_ASSERT(it.value().type() != KisMetaData::Value::Structure); // Can't nest structure
QString key = QString("%1/%2:%3").arg(name).arg(structPrefix.c_str()).arg(it.key());
Exiv2::XmpKey ekey(prefix, key.toLatin1().constData());
dbgFile << ppVar(key) << ppVar(ekey.key().c_str());
Exiv2::Value *v = kmdValueToExivXmpValue(it.value());
if (v) {
xmpData_.add(ekey, v);
}
}
}
}
bool KisXMPIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
dbgFile << "Save XMP Data";
Exiv2::XmpData xmpData_;
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
const KisMetaData::Entry& entry = *it;
// Check whether the prefix and namespace are know to exiv2
std::string prefix = exiv2Prefix(entry.schema());
dbgFile << "Saving " << entry.name();
const KisMetaData::Value& value = entry.value();
const KisMetaData::TypeInfo* typeInfo = entry.schema()->propertyType(entry.name());
if (value.type() == KisMetaData::Value::Structure) {
QMap<QString, KisMetaData::Value> structure = value.asStructure();
const KisMetaData::Schema* structureSchema = 0;
if (typeInfo) {
structureSchema = typeInfo->structureSchema();
}
if (!structureSchema) {
dbgFile << "Unknown schema for " << entry.name();
structureSchema = entry.schema();
}
Q_ASSERT(structureSchema);
saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema);
} else {
Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData());
if (typeInfo && (typeInfo->propertyType() == KisMetaData::TypeInfo::OrderedArrayType
|| typeInfo->propertyType() == KisMetaData::TypeInfo::UnorderedArrayType
|| typeInfo->propertyType() == KisMetaData::TypeInfo::AlternativeArrayType)
&& typeInfo->embeddedPropertyType()->propertyType() == KisMetaData::TypeInfo::StructureType) {
// Here is the bad part, again we need to do it by hand
Exiv2::XmpTextValue tv;
switch (typeInfo->propertyType()) {
case KisMetaData::TypeInfo::OrderedArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
break;
case KisMetaData::TypeInfo::UnorderedArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
break;
case KisMetaData::TypeInfo::AlternativeArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaAlt);
break;
default:
// Cannot happen
;
}
xmpData_.add(key, &tv); // set the arrya type
const KisMetaData::TypeInfo* stuctureTypeInfo = typeInfo->embeddedPropertyType();
const KisMetaData::Schema* structureSchema = 0;
if (stuctureTypeInfo) {
structureSchema = stuctureTypeInfo->structureSchema();
}
if (!structureSchema) {
dbgFile << "Unknown schema for " << entry.name();
structureSchema = entry.schema();
}
Q_ASSERT(structureSchema);
QList<KisMetaData::Value> array = value.asArray();
for (int idx = 0; idx < array.size(); ++idx) {
saveStructure(xmpData_, QString("%1[%2]").arg(entry.name()).arg(idx + 1), prefix, array[idx].asStructure(), structureSchema);
}
} else {
dbgFile << ppVar(key.key().c_str());
Exiv2::Value *v = kmdValueToExivXmpValue(value);
if (v) {
xmpData_.add(key, v);
}
}
}
// TODO property qualifier
}
// Serialize data
std::string xmpPacket_;
Exiv2::XmpParser::encode(xmpPacket_, xmpData_);
// Save data into the IO device
ioDevice->open(QIODevice::WriteOnly);
if (headerType == KisMetaData::IOBackend::JpegHeader) {
xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_;
}
ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length());
return true;
}
bool parseTagName(const QString &tagString,
QString &structName,
int &arrayIndex,
QString &tagName,
const KisMetaData::TypeInfo* typeInfo,
const KisMetaData::Schema *schema)
{
arrayIndex = -1;
typeInfo = 0;
int numSubNames = tagString.count('/') + 1;
if (numSubNames == 1) {
structName.clear();
tagName = tagString;
typeInfo = schema->propertyType(tagName);
return true;
}
if (numSubNames == 2) {
QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)");
if (regexp.indexIn(tagString) != -1) {
structName = regexp.capturedTexts()[1];
tagName = regexp.capturedTexts()[3];
typeInfo = schema->propertyType(structName);
if (typeInfo && typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
typeInfo = typeInfo->structureSchema()->propertyType(tagName);
}
return true;
}
QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)");
if (regexp2.indexIn(tagString) != -1) {
structName = regexp2.capturedTexts()[1];
arrayIndex = regexp2.capturedTexts()[2].toInt() - 1;
tagName = regexp2.capturedTexts()[4];
if (schema->propertyType(structName)) {
typeInfo = schema->propertyType(structName)->embeddedPropertyType();
Q_ASSERT(typeInfo);
if (typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
typeInfo = typeInfo->structureSchema()->propertyType(tagName);
}
}
return true;
}
}
- qWarning() << "WARNING: Unsupported tag. We do not yet support nested tags. The tag will be dropped!";
- qWarning() << " Failing tag:" << tagString;
+ warnKrita << "WARNING: Unsupported tag. We do not yet support nested tags. The tag will be dropped!";
+ warnKrita << " Failing tag:" << tagString;
return false;
}
bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
ioDevice->open(QIODevice::ReadOnly);
dbgFile << "Load XMP Data";
std::string xmpPacket_;
QByteArray arr = ioDevice->readAll();
xmpPacket_.assign(arr.data(), arr.length());
dbgFile << xmpPacket_.length();
// dbgFile << xmpPacket_.c_str();
Exiv2::XmpData xmpData_;
Exiv2::XmpParser::decode(xmpData_, xmpPacket_);
QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > > structures;
QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > > arraysOfStructures;
for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) {
dbgFile << "Start iteration" << it->key().c_str();
Exiv2::XmpKey key(it->key());
dbgFile << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str();
if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") { // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated
dbgFile << "dropped";
} else {
const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str());
if (!schema) {
schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str());
if (!schema) {
schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str());
Q_ASSERT(schema);
}
}
const Exiv2::Value::AutoPtr value = it->getValue();
QString structName;
int arrayIndex = -1;
QString tagName;
const KisMetaData::TypeInfo* typeInfo = 0;
if (!parseTagName(key.tagName().c_str(),
structName, arrayIndex, tagName,
typeInfo, schema)) continue;
bool isStructureEntry = !structName.isEmpty() && arrayIndex == -1;
bool isStructureInArrayEntry = !structName.isEmpty() && arrayIndex != -1;
Q_ASSERT(isStructureEntry != isStructureInArrayEntry || !isStructureEntry);
KisMetaData::Value v;
bool ignoreValue = false;
// Compute the value
if (value->typeId() == Exiv2::xmpBag
|| value->typeId() == Exiv2::xmpSeq
|| value->typeId() == Exiv2::xmpAlt) {
const KisMetaData::TypeInfo* embeddedTypeInfo = 0;
if (typeInfo) {
embeddedTypeInfo = typeInfo->embeddedPropertyType();
}
const KisMetaData::Parser* parser = 0;
if (embeddedTypeInfo) {
parser = embeddedTypeInfo->parser();
}
const Exiv2::XmpArrayValue* xav = dynamic_cast<const Exiv2::XmpArrayValue*>(value.get());
Q_ASSERT(xav);
QList<KisMetaData::Value> array;
for (std::vector< std::string >::const_iterator it = xav->value_.begin();
it != xav->value_.end(); ++it) {
QString value = it->c_str();
if (parser) {
array.push_back(parser->parse(value));
} else {
dbgImage << "No parser " << tagName;
array.push_back(KisMetaData::Value(value));
}
}
KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid;
switch (xav->xmpArrayType()) {
case Exiv2::XmpValue::xaNone:
warnKrita << "KisXMPIO: Unsupported array";
break;
case Exiv2::XmpValue::xaAlt:
vt = KisMetaData::Value::AlternativeArray;
break;
case Exiv2::XmpValue::xaBag:
vt = KisMetaData::Value::UnorderedArray;
break;
case Exiv2::XmpValue::xaSeq:
vt = KisMetaData::Value::OrderedArray;
break;
}
v = KisMetaData::Value(array, vt);
} else if (value->typeId() == Exiv2::langAlt) {
const Exiv2::LangAltValue* xav = dynamic_cast<const Exiv2::LangAltValue*>(value.get());
QList<KisMetaData::Value> alt;
for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin();
it != xav->value_.end(); ++it) {
KisMetaData::Value valt(it->second.c_str());
valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str()));
alt.push_back(valt);
}
v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
} else {
QString valTxt = value->toString().c_str();
if (typeInfo && typeInfo->parser()) {
v = typeInfo->parser()->parse(valTxt);
} else {
dbgFile << "No parser " << tagName;
v = KisMetaData::Value(valTxt);
}
if (valTxt == "type=\"Struct\"") {
if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
ignoreValue = true;
}
}
}
// set the value
if (isStructureEntry) {
structures[schema][structName][tagName] = v;
} else if (isStructureInArrayEntry) {
if (arraysOfStructures[schema][structName].size() <= arrayIndex) {
arraysOfStructures[schema][structName].resize(arrayIndex + 1);
}
if (!arraysOfStructures[schema][structName][arrayIndex].contains(tagName)) {
arraysOfStructures[schema][structName][arrayIndex][tagName] = v;
} else {
- qWarning() << "WARNING: trying to overwrite tag" << tagName << "in" << structName << arrayIndex;
+ warnKrita << "WARNING: trying to overwrite tag" << tagName << "in" << structName << arrayIndex;
}
} else {
if (!ignoreValue) {
store->addEntry(KisMetaData::Entry(schema, tagName, v));
} else {
dbgFile << "Ignoring value for " << tagName << " " << v;
}
}
}
}
for (QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > >::iterator it = structures.begin();
it != structures.end(); ++it) {
const KisMetaData::Schema* schema = it.key();
for (QMap<QString, QMap<QString, KisMetaData::Value> >::iterator it2 = it.value().begin();
it2 != it.value().end(); ++it2) {
store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value())));
}
}
for (QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) {
const KisMetaData::Schema* schema = it.key();
for (QMap<QString, QVector<QMap<QString, KisMetaData::Value> > >::iterator it2 = it.value().begin();
it2 != it.value().end(); ++it2) {
KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray;
QString entryName = it2.key();
if (schema->propertyType(entryName)) {
switch (schema->propertyType(entryName)->propertyType()) {
case KisMetaData::TypeInfo::OrderedArrayType:
type = KisMetaData::Value::OrderedArray;
break;
case KisMetaData::TypeInfo::UnorderedArrayType:
type = KisMetaData::Value::OrderedArray;
break;
case KisMetaData::TypeInfo::AlternativeArrayType:
type = KisMetaData::Value::AlternativeArray;
break;
default:
type = KisMetaData::Value::Invalid;
break;
}
} else if (store->containsEntry(schema, entryName)) {
KisMetaData::Value value = store->getEntry(schema, entryName).value();
if (value.isArray()) {
type = value.type();
}
}
store->removeEntry(schema, entryName);
if (type != KisMetaData::Value::Invalid) {
QList< KisMetaData::Value > valueList;
for (int i = 0; i < it2.value().size(); ++i) {
valueList.append(it2.value()[i]);
}
store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type)));
}
}
}
return true;
}
diff --git a/krita/ui/kra/kis_kra_load_visitor.cpp b/krita/ui/kra/kis_kra_load_visitor.cpp
index 247b49d9e5f..a31e997499f 100644
--- a/krita/ui/kra/kis_kra_load_visitor.cpp
+++ b/krita/ui/kra/kis_kra_load_visitor.cpp
@@ -1,496 +1,496 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kra/kis_kra_load_visitor.h"
#include "kis_kra_tags.h"
#include "flake/kis_shape_layer.h"
#include <QRect>
#include <QBuffer>
#include <QByteArray>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoStore.h>
#include <KoColorSpace.h>
// kritaimage
#include <metadata/kis_meta_data_io_backend.h>
#include <metadata/kis_meta_data_store.h>
#include <kis_types.h>
#include <kis_node_visitor.h>
#include <kis_image.h>
#include <kis_selection.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_adjustment_layer.h>
#include <filter/kis_filter_configuration.h>
#include <kis_datamanager.h>
#include <generator/kis_generator_layer.h>
#include <kis_pixel_selection.h>
#include <kis_clone_layer.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_transform_mask_params_interface.h>
#include "kis_transform_mask_params_factory_registry.h"
#include <kis_transparency_mask.h>
#include <kis_selection_mask.h>
#include "kis_shape_selection.h"
#include "kis_dom_utils.h"
using namespace KRA;
QString expandEncodedDirectory(const QString& _intern)
{
QString intern = _intern;
QString result;
int pos;
while ((pos = intern.indexOf('/')) != -1) {
if (QChar(intern.at(0)).isDigit())
result += "part";
result += intern.left(pos + 1); // copy numbers (or "pictures") + "/"
intern = intern.mid(pos + 1); // remove the dir we just processed
}
if (!intern.isEmpty() && QChar(intern.at(0)).isDigit())
result += "part";
result += intern;
return result;
}
KisKraLoadVisitor::KisKraLoadVisitor(KisImageWSP image,
KoStore *store,
QMap<KisNode *, QString> &layerFilenames,
const QString & name,
int syntaxVersion) :
KisNodeVisitor(),
m_layerFilenames(layerFilenames)
{
m_external = false;
m_image = image;
m_store = store;
m_name = name;
m_store->pushDirectory();
if (m_name.startsWith("/")) {
m_name.remove(0, 1);
}
if (!m_store->enterDirectory(m_name)) {
QStringList directories = m_store->directoryList();
- qDebug() << directories;
+ dbgKrita << directories;
if (directories.size() > 0) {
dbgFile << "Could not locate the directory, maybe some encoding issue? Grab the first directory, that'll be the image one." << m_name << directories;
m_name = directories.first();
}
else {
dbgFile << "Could not enter directory" << m_name << ", probably an old-style file with 'part' added.";
m_name = expandEncodedDirectory(m_name);
}
}
else {
m_store->popDirectory();
}
m_syntaxVersion = syntaxVersion;
}
void KisKraLoadVisitor::setExternalUri(const QString &uri)
{
m_external = true;
m_uri = uri;
}
bool KisKraLoadVisitor::visit(KisExternalLayer * layer)
{
bool result = false;
if (KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(layer)) {
if (!loadMetaData(layer)) {
return false;
}
m_store->pushDirectory();
m_store->enterDirectory(getLocation(layer, DOT_SHAPE_LAYER)) ;
result = shapeLayer->loadLayer(m_store);
m_store->popDirectory();
}
result = visitAll(layer) && result;
return result;
}
bool KisKraLoadVisitor::visit(KisPaintLayer *layer)
{
dbgFile << "Visit: " << layer->name() << " colorSpace: " << layer->colorSpace()->id();
if (!loadPaintDevice(layer->paintDevice(), getLocation(layer))) {
return false;
}
if (!loadProfile(layer->paintDevice(), getLocation(layer, DOT_ICC))) {
return false;
}
if (!loadMetaData(layer)) {
return false;
}
if (m_syntaxVersion == 1) {
// Check whether there is a file with a .mask extension in the
// layer directory, if so, it's an old-style transparency mask
// that should be converted.
QString location = getLocation(layer, ".mask");
if (m_store->open(location)) {
KisSelectionSP selection = KisSelectionSP(new KisSelection());
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
if (!pixelSelection->read(m_store->device())) {
pixelSelection->disconnect();
} else {
KisTransparencyMask* mask = new KisTransparencyMask();
mask->setSelection(selection);
m_image->addNode(mask, layer, layer->firstChild());
}
m_store->close();
}
}
bool result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisGroupLayer *layer)
{
if (!loadMetaData(layer)) {
return false;
}
bool result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisAdjustmentLayer* layer)
{
// Adjustmentlayers are tricky: there's the 1.x style and the 2.x
// style, which has selections with selection components
bool result = true;
if (m_syntaxVersion == 1) {
KisSelectionSP selection = new KisSelection();
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
result = loadPaintDevice(pixelSelection, getLocation(layer, ".selection"));
layer->setInternalSelection(selection);
} else if (m_syntaxVersion == 2) {
result = loadSelection(getLocation(layer), layer->internalSelection());
} else {
// We use the default, empty selection
}
if (!loadMetaData(layer)) {
return false;
}
loadFilterConfiguration(layer->filter().data(), getLocation(layer, DOT_FILTERCONFIG));
result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisGeneratorLayer* layer)
{
if (!loadMetaData(layer)) {
return false;
}
bool result = true;
result = loadSelection(getLocation(layer), layer->internalSelection());
result = loadFilterConfiguration(layer->filter().data(), getLocation(layer, DOT_FILTERCONFIG));
layer->update();
result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisCloneLayer *layer)
{
if (!loadMetaData(layer)) {
return false;
}
// the layer might have already been lazily initialized
// from the mask loading code
if (layer->copyFrom()) {
return true;
}
KisNodeSP srcNode = layer->copyFromInfo().findNode(m_image->rootLayer());
KisLayerSP srcLayer = dynamic_cast<KisLayer*>(srcNode.data());
Q_ASSERT(srcLayer);
layer->setCopyFrom(srcLayer);
// Clone layers have no data except for their masks
bool result = visitAll(layer);
return result;
}
void KisKraLoadVisitor::initSelectionForMask(KisMask *mask)
{
KisLayer *cloneLayer = dynamic_cast<KisCloneLayer*>(mask->parent().data());
if (cloneLayer) {
// the clone layers should be initialized out of order
// and lazily, because their original() is still not
// initialized
cloneLayer->accept(*this);
}
KisLayer *parentLayer = dynamic_cast<KisLayer*>(mask->parent().data());
// the KisKraLoader must have already set the parent for us
Q_ASSERT(parentLayer);
mask->initSelection(parentLayer);
}
bool KisKraLoadVisitor::visit(KisFilterMask *mask)
{
initSelectionForMask(mask);
bool result = true;
result = loadSelection(getLocation(mask), mask->selection());
result = loadFilterConfiguration(mask->filter().data(), getLocation(mask, DOT_FILTERCONFIG));
return result;
}
bool KisKraLoadVisitor::visit(KisTransformMask *mask)
{
QString location = getLocation(mask, DOT_TRANSFORMCONFIG);
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
if (!data.isEmpty()) {
QDomDocument doc;
doc.setContent(data);
QDomElement rootElement = doc.documentElement();
QDomElement main;
if (!KisDomUtils::findOnlyElement(rootElement, "main", &main/*, &m_errorMessages*/)) {
return false;
}
QString id = main.attribute("id", "not-valid");
if (id == "not-valid") {
m_errorMessages << i18n("Could not load \"id\" of the transform mask");
return false;
}
QDomElement data;
if (!KisDomUtils::findOnlyElement(rootElement, "data", &data, &m_errorMessages)) {
return false;
}
KisTransformMaskParamsInterfaceSP params =
KisTransformMaskParamsFactoryRegistry::instance()->createParams(id, data);
if (!params) {
m_errorMessages << i18n("Could not create transform mask params");
return false;
}
mask->setTransformParams(params);
return true;
}
}
return false;
}
bool KisKraLoadVisitor::visit(KisTransparencyMask *mask)
{
initSelectionForMask(mask);
return loadSelection(getLocation(mask), mask->selection());
}
bool KisKraLoadVisitor::visit(KisSelectionMask *mask)
{
initSelectionForMask(mask);
return loadSelection(getLocation(mask), mask->selection());
}
QStringList KisKraLoadVisitor::errorMessages() const
{
return m_errorMessages;
}
bool KisKraLoadVisitor::loadPaintDevice(KisPaintDeviceSP device, const QString& location)
{
// Layer data
if (m_store->open(location)) {
if (!device->read(m_store->device())) {
m_errorMessages << i18n("Could not read pixel data: %1.", location);
device->disconnect();
m_store->close();
return false;
}
m_store->close();
} else {
m_errorMessages << i18n("Could not load pixel data: %1.", location);
return false;
}
if (m_store->open(location + ".defaultpixel")) {
int pixelSize = device->colorSpace()->pixelSize();
if (m_store->size() == pixelSize) {
quint8 *defPixel = new quint8[pixelSize];
m_store->read((char*)defPixel, pixelSize);
device->setDefaultPixel(defPixel);
delete[] defPixel;
}
m_store->close();
}
return true;
}
bool KisKraLoadVisitor::loadProfile(KisPaintDeviceSP device, const QString& location)
{
if (m_store->hasFile(location)) {
m_store->open(location);
QByteArray data; data.resize(m_store->size());
dbgFile << "Data to load: " << m_store->size() << " from " << location << " with color space " << device->colorSpace()->id();
int read = m_store->read(data.data(), m_store->size());
dbgFile << "Profile size: " << data.size() << " " << m_store->atEnd() << " " << m_store->device()->bytesAvailable() << " " << read;
m_store->close();
// Create a colorspace with the embedded profile
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), data);
const KoColorSpace *cs =
KoColorSpaceRegistry::instance()->colorSpace(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), profile);
// replace the old colorspace
if (cs) {
device->setDataManager(device->dataManager(), cs);
return true;
}
}
m_errorMessages << i18n("Could not load profile %1.", location);
return false;
}
bool KisKraLoadVisitor::loadFilterConfiguration(KisFilterConfiguration* kfc, const QString& location)
{
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
if (!data.isEmpty()) {
QString xml(data);
QDomDocument doc;
doc.setContent(data);
QDomElement e = doc.documentElement();
if (e.tagName() == "filterconfig") {
kfc->fromLegacyXML(e);
} else {
kfc->fromXML(e);
}
return true;
}
}
m_errorMessages << i18n("Could not filter configuration %1.", location);
return false;
}
bool KisKraLoadVisitor::loadMetaData(KisNode* node)
{
dbgFile << "Load metadata for " << node->name();
KisLayer* layer = qobject_cast<KisLayer*>(node);
if (!layer) return true;
bool result = true;
KisMetaData::IOBackend* backend = KisMetaData::IOBackendRegistry::instance()->get("xmp");
if (!backend || !backend->supportLoading()) {
if (backend)
dbgFile << "Backend " << backend->id() << " does not support loading.";
else
dbgFile << "Could not load the XMP backenda t all";
return true;
}
QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA);
dbgFile << "going to load " << backend->id() << ", " << backend->name() << " from " << location;
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
QBuffer buffer(&data);
if (!backend->loadFrom(layer->metaData(), &buffer)) {
m_errorMessages << i18n("Could not load metadata for layer %1.", layer->name());
result = false;
}
}
return result;
}
bool KisKraLoadVisitor::loadSelection(const QString& location, KisSelectionSP dstSelection)
{
// Pixel selection
bool result = true;
QString pixelSelectionLocation = location + DOT_PIXEL_SELECTION;
if (m_store->hasFile(pixelSelectionLocation)) {
KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection();
result = loadPaintDevice(pixelSelection, pixelSelectionLocation);
if (!result) {
m_errorMessages << i18n("Could not load raster selection %1.", location);
}
pixelSelection->invalidateOutlineCache();
}
// Shape selection
QString shapeSelectionLocation = location + DOT_SHAPE_SELECTION;
if (m_store->hasFile(shapeSelectionLocation + "/content.xml")) {
m_store->pushDirectory();
m_store->enterDirectory(shapeSelectionLocation) ;
KisShapeSelection* shapeSelection = new KisShapeSelection(m_image, dstSelection);
dstSelection->setShapeSelection(shapeSelection);
result = shapeSelection->loadSelection(m_store);
m_store->popDirectory();
if (!result) {
m_errorMessages << i18n("Could not load vector selection %1.", location);
}
}
return result;
}
QString KisKraLoadVisitor::getLocation(KisNode* node, const QString& suffix)
{
QString location = m_external ? QString() : m_uri;
location += m_name + LAYER_PATH + m_layerFilenames[node] + suffix;
return location;
}
diff --git a/krita/ui/kra/kis_kra_loader.cpp b/krita/ui/kra/kis_kra_loader.cpp
index d257013481a..1ef0d12f3ca 100644
--- a/krita/ui/kra/kis_kra_loader.cpp
+++ b/krita/ui/kra/kis_kra_loader.cpp
@@ -1,942 +1,942 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2007
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kra/kis_kra_loader.h"
#include <QApplication>
#include <QStringList>
#include <QMessageBox>
#include <kurl.h>
#include <KoStore.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoDocumentInfo.h>
#include <KoFileDialog.h>
#include <KisImportExportManager.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator.h>
#include <generator/kis_generator_layer.h>
#include <generator/kis_generator_registry.h>
#include <kis_adjustment_layer.h>
#include <kis_annotation.h>
#include <kis_base_node.h>
#include <kis_clone_layer.h>
#include <kis_debug.h>
#include <kis_assert.h>
#include <kis_external_layer_iface.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_name_server.h>
#include <kis_paint_layer.h>
#include <kis_selection.h>
#include <kis_selection_mask.h>
#include <kis_shape_layer.h>
#include <kis_transparency_mask.h>
#include <kis_layer_composition.h>
#include <kis_file_layer.h>
#include <kis_psd_layer_style.h>
#include <kis_psd_layer_style_resource.h>
#include "kis_resource_server_provider.h"
#include "KisDocument.h"
#include "kis_config.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_kra_load_visitor.h"
/*
Color model id comparison through the ages:
2.4 2.5 2.6 ideal
ALPHA ALPHA ALPHA ALPHAU8
CMYK CMYK CMYK CMYKAU8
CMYKAF32 CMYKAF32
CMYKA16 CMYKAU16 CMYKAU16
GRAYA GRAYA GRAYA GRAYAU8
GrayF32 GRAYAF32 GRAYAF32
GRAYA16 GRAYAU16 GRAYAU16
LABA LABA LABA LABAU16
LABAF32 LABAF32
LABAU8 LABAU8
RGBA RGBA RGBA RGBAU8
RGBA16 RGBA16 RGBA16 RGBAU16
RgbAF32 RGBAF32 RGBAF32
RgbAF16 RgbAF16 RGBAF16
XYZA16 XYZA16 XYZA16 XYZAU16
XYZA8 XYZA8 XYZAU8
XyzAF16 XyzAF16 XYZAF16
XyzAF32 XYZAF32 XYZAF32
YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8
YCbCrAU16 YCBCRAU16 YCBCRAU16
YCBCRF32 YCBCRF32
*/
using namespace KRA;
struct KisKraLoader::Private
{
public:
KisDocument* document;
QString imageName; // used to be stored in the image, is now in the documentInfo block
QString imageComment; // used to be stored in the image, is now in the documentInfo block
QMap<KisNode*, QString> layerFilenames; // temp storage during loading
int syntaxVersion; // version of the fileformat we are loading
vKisNodeSP selectedNodes; // the nodes that were active when saving the document.
QMap<QString, QString> assistantsFilenames;
QList<KisPaintingAssistant*> assistants;
QStringList errorMessages;
};
void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) {
if (colorspacename == "Grayscale + Alpha") {
colorspacename = "GRAYA";
profileProductName.clear();
}
else if (colorspacename == "RgbAF32") {
colorspacename = "RGBAF32";
profileProductName.clear();
}
else if (colorspacename == "RgbAF16") {
colorspacename = "RGBAF16";
profileProductName.clear();
}
else if (colorspacename == "CMYKA16") {
colorspacename = "CMYKAU16";
}
else if (colorspacename == "GrayF32") {
colorspacename = "GRAYAF32";
profileProductName.clear();
}
else if (colorspacename == "GRAYA16") {
colorspacename = "GRAYAU16";
}
else if (colorspacename == "XyzAF16") {
colorspacename = "XYZAF16";
profileProductName.clear();
}
else if (colorspacename == "XyzAF32") {
colorspacename = "XYZAF32";
profileProductName.clear();
}
else if (colorspacename == "YCbCrA") {
colorspacename = "YCBCRA8";
}
else if (colorspacename == "YCbCrAU16") {
colorspacename = "YCBCRAU16";
}
}
KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion)
: m_d(new Private())
{
m_d->document = document;
m_d->syntaxVersion = syntaxVersion;
}
KisKraLoader::~KisKraLoader()
{
delete m_d;
}
KisImageWSP KisKraLoader::loadXML(const KoXmlElement& element)
{
QString attr;
KisImageWSP image = 0;
QString name;
qint32 width;
qint32 height;
QString profileProductName;
double xres;
double yres;
QString colorspacename;
const KoColorSpace * cs;
if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) {
if ((m_d->imageName = element.attribute(NAME)).isNull()) {
m_d->errorMessages << i18n("Image does not have a name.");
return KisImageWSP(0);
}
if ((attr = element.attribute(WIDTH)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a width.");
return KisImageWSP(0);
}
width = attr.toInt();
if ((attr = element.attribute(HEIGHT)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a height.");
return KisImageWSP(0);
}
height = attr.toInt();
m_d->imageComment = element.attribute(DESCRIPTION);
xres = 100.0 / 72.0;
if (!(attr = element.attribute(X_RESOLUTION)).isNull()) {
if (attr.toDouble() > 1.0) {
xres = attr.toDouble() / 72.0;
}
}
yres = 100.0 / 72.0;
if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) {
if (attr.toDouble() > 1.0) {
yres = attr.toDouble() / 72.0;
}
}
if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) {
// An old file: take a reasonable default.
// Krita didn't support anything else in those
// days anyway.
colorspacename = "RGBA";
}
profileProductName = element.attribute(PROFILE);
// A hack for an old colorspacename
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
if (profileProductName.isNull()) {
// no mention of profile so get default profile";
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
} else {
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName);
}
if (cs == 0) {
// try once more without the profile
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
if (cs == 0) {
m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename);
return KisImageWSP(0);
}
}
if (m_d->document) {
image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name);
}
else {
image = new KisImage(0, width, height, cs, name);
}
image->setResolution(xres, yres);
loadNodes(element, image, const_cast<KisGroupLayer*>(image->rootLayer().data()));
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == "ProjectionBackgroundColor") {
if (e.hasAttribute("ColorData")) {
QByteArray colorData = QByteArray::fromBase64(e.attribute("ColorData").toLatin1());
KoColor color((const quint8*)colorData.data(), image->colorSpace());
image->setDefaultProjectionColor(color);
}
}
}
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == "compositions") {
loadCompositions(e, image);
}
}
}
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if (e.tagName() == "assistants") {
loadAssistantsList(e);
}
}
return image;
}
void KisKraLoader::loadBinaryData(KoStore * store, KisImageWSP image, const QString & uri, bool external)
{
// icc profile: if present, this overrides the profile product name loaded in loadXML.
QString location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->hasFile(location)) {
if (store->open(location)) {
QByteArray data; data.resize(store->size());
bool res = (store->read(data.data(), store->size()) > -1);
store->close();
if (res) {
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data);
if (profile && profile->valid()) {
res = image->assignImageProfile(profile);
}
if (!res) {
profile = KoColorSpaceRegistry::instance()->profileByName(KoColorSpaceRegistry::instance()->colorSpaceFactory(image->colorSpace()->id())->defaultProfile());
Q_ASSERT(profile && profile->valid());
image->assignImageProfile(profile);
}
}
}
}
// Load the layers data: if there is a profile associated with a layer it will be set now.
KisKraLoadVisitor visitor(image, store, m_d->layerFilenames, m_d->imageName, m_d->syntaxVersion);
if (external) {
visitor.setExternalUri(uri);
}
image->rootLayer()->accept(visitor);
if (!visitor.errorMessages().isEmpty()) {
m_d->errorMessages.append(visitor.errorMessages());
}
// annotations
// exif
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->hasFile(location)) {
QByteArray data;
store->open(location);
data = store->read(store->size());
store->close();
image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data)));
}
// layer styles
location = external ? QString() : uri;
location += m_d->imageName + LAYER_STYLES_PATH;
if (store->hasFile(location)) {
KisPSDLayerStyleCollectionResource *collection =
new KisPSDLayerStyleCollectionResource("Embedded Styles.asl");
collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName));
KIS_ASSERT_RECOVER_NOOP(!collection->valid());
store->open(location);
{
KoStoreDevice device(store);
device.open(QIODevice::ReadOnly);
/**
* ASL loading code cannot work with non-sequential IO devices,
* so convert the device beforehand!
*/
QByteArray buf = device.readAll();
QBuffer raDevice(&buf);
raDevice.open(QIODevice::ReadOnly);
collection->loadFromDevice(&raDevice);
}
store->close();
if (collection->valid()) {
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
server->addResource(collection, false);
collection->assignAllLayerStyles(image->root());
} else {
- qWarning() << "WARNING: Couldn't load layer styles library from .kra!";
+ warnKrita << "WARNING: Couldn't load layer styles library from .kra!";
delete collection;
}
}
if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull())
m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName);
if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull())
m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment);
loadAssistants(store, uri, external);
}
vKisNodeSP KisKraLoader::selectedNodes() const
{
return m_d->selectedNodes;
}
QList<KisPaintingAssistant *> KisKraLoader::assistants() const
{
return m_d->assistants;
}
QStringList KisKraLoader::errorMessages() const
{
return m_d->errorMessages;
}
void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external)
{
QString file_path;
QString location;
QMap<int ,KisPaintingAssistantHandleSP> handleMap;
KisPaintingAssistant* assistant = 0;
QMap<QString,QString>::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin();
while (loadedAssistant != m_d->assistantsFilenames.constEnd()){
const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value());
if (factory) {
assistant = factory->createPaintingAssistant();
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
file_path = location + loadedAssistant.key();
assistant->loadXml(store, handleMap, file_path);
//If an assistant has too few handles than it should according to it's own setup, just don't load it//
if (assistant->handles().size()==assistant->numHandles()){
m_d->assistants.append(assistant);
}
}
loadedAssistant++;
}
}
KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent)
{
KoXmlNode node = element.firstChild();
KoXmlNode child;
if (!node.isNull()) {
if (node.isElement()) {
if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) {
for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) {
KisNodeSP node = loadNode(child.toElement(), image, parent);
if (node) {
image->nextLayerName(); // Make sure the nameserver is current with the number of nodes.
image->addNode(node, parent);
if (node->inherits("KisLayer") && child.childNodesCount() > 0) {
loadNodes(child.toElement(), image, node);
}
}
}
}
}
}
return parent;
}
KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent)
{
// Nota bene: If you add new properties to layers, you should
// ALWAYS define a default value in case the property is not
// present in the layer definition: this helps a LOT with backward
// compatibility.
QString name = element.attribute(NAME, "No Name");
QUuid id = QUuid(element.attribute(UUID, QUuid().toString()));
qint32 x = element.attribute(X, "0").toInt();
qint32 y = element.attribute(Y, "0").toInt();
qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt();
if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8;
if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8;
const KoColorSpace* colorSpace = 0;
if ((element.attribute(COLORSPACE_NAME)).isNull()) {
dbgFile << "No attribute color space for layer: " << name;
colorSpace = image->colorSpace();
}
else {
QString colorspacename = element.attribute(COLORSPACE_NAME);
QString profileProductName;
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name;
// use default profile - it will be replaced later in completeLoading
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
dbgFile << "found colorspace" << colorSpace;
if (!colorSpace) {
m_d->errorMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename);
return 0;
}
}
bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true;
bool locked = element.attribute(LOCKED, "0") == "0" ? false : true;
bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true;
// Now find out the layer type and do specific handling
QString nodeType;
if (m_d->syntaxVersion == 1) {
nodeType = element.attribute("layertype");
if (nodeType.isEmpty()) {
nodeType = PAINT_LAYER;
}
}
else {
nodeType = element.attribute(NODE_TYPE);
}
if (nodeType.isEmpty()) {
m_d->errorMessages << i18n("Layer %1 has an unsupported type.", name);
return 0;
}
KisNodeSP node = 0;
if (nodeType == PAINT_LAYER)
node = loadPaintLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GROUP_LAYER)
node = loadGroupLayer(element, image, name, colorSpace, opacity);
else if (nodeType == ADJUSTMENT_LAYER)
node = loadAdjustmentLayer(element, image, name, colorSpace, opacity);
else if (nodeType == SHAPE_LAYER)
node = loadShapeLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GENERATOR_LAYER)
node = loadGeneratorLayer(element, image, name, colorSpace, opacity);
else if (nodeType == CLONE_LAYER)
node = loadCloneLayer(element, image, name, colorSpace, opacity);
else if (nodeType == FILTER_MASK)
node = loadFilterMask(element, parent);
else if (nodeType == TRANSFORM_MASK)
node = loadTransformMask(element, parent);
else if (nodeType == TRANSPARENCY_MASK)
node = loadTransparencyMask(element, parent);
else if (nodeType == SELECTION_MASK)
node = loadSelectionMask(image, element, parent);
else if (nodeType == FILE_LAYER) {
node = loadFileLayer(element, image, name, opacity);
}
else {
m_d->errorMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType);
return 0;
}
// Loading the node went wrong. Return empty node and leave to
// upstream to complain to the user
if (!node) {
m_d->errorMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType);
return 0;
}
node->setVisible(visible, true);
node->setUserLocked(locked);
node->setCollapsed(collapsed);
node->setX(x);
node->setY(y);
node->setName(name);
if (! id.isNull()) // if no uuid in file, new one has been generated already
node->setUuid(id);
if (node->inherits("KisLayer")) {
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount());
QString compositeOpName = element.attribute(COMPOSITE_OP, "normal");
layer->setChannelFlags(channelFlags);
layer->setCompositeOp(compositeOpName);
if (element.hasAttribute(LAYER_STYLE_UUID)) {
QString uuidString = element.attribute(LAYER_STYLE_UUID);
QUuid uuid(uuidString);
if (!uuid.isNull()) {
KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
dumbLayerStyle->setUuid(uuid);
layer->setLayerStyle(dumbLayerStyle);
} else {
- qWarning() << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
+ warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
}
}
}
if (node->inherits("KisGroupLayer")) {
if (element.hasAttribute(PASS_THROUGH_MODE)) {
bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0";
KisGroupLayer *group = qobject_cast<KisGroupLayer*>(node.data());
group->setPassThroughMode(value);
}
}
if (node->inherits("KisPaintLayer")) {
KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(node.data());
QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
layer->setChannelLockFlags(channelLockFlags);
}
if (element.attribute(FILE_NAME).isNull()) {
m_d->layerFilenames[node.data()] = name;
}
else {
m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME);
}
if (element.hasAttribute("selected") && element.attribute("selected") == "true") {
m_d->selectedNodes.append(node);
}
return node;
}
KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
KisPaintLayer* layer;
layer = new KisPaintLayer(image, name, opacity, cs);
Q_CHECK_PTR(layer);
// Load exif info
/*TODO: write and use the legacy stuff to load that exif tag
for( KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() )
{
KoXmlElement e = node.toElement();
if ( !e.isNull() && e.tagName() == "ExifInfo" )
{
layer->paintDevice()->exifInfo()->load(e);
}
}*/
// TODO load metadata
return layer;
}
KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, quint32 opacity)
{
QString filename = element.attribute("source", QString());
if (filename.isNull()) return 0;
bool scale = (element.attribute("scale", "true") == "true");
int scalingMethod = element.attribute("scalingmethod", "-1").toInt();
if (scalingMethod < 0) {
if (scale) {
scalingMethod = KisFileLayer::ToImagePPI;
}
else {
scalingMethod = KisFileLayer::None;
}
}
QString documentPath;
if (m_d->document) {
documentPath = m_d->document->url().toLocalFile();
}
QFileInfo info(documentPath);
QString basePath = info.absolutePath();
QString fullPath = basePath + QDir::separator() + filename;
// Entering the event loop to show the messagebox will delete the image, so up the ref by one
image->ref();
if (!QFileInfo(fullPath).exists()) {
qApp->setOverrideCursor(Qt::ArrowCursor);
QString msg = i18nc(
"@info",
"The file associated to a file layer with the name \"%1\" is not found.<nl/><nl/>"
"Expected path:<nl/>"
"%2<nl/><nl/>"
"Do you want to locate it manually?", name, fullPath);
int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Import));
dialog.setDefaultDir(basePath);
QString url = dialog.url();
if (!QFileInfo(basePath).exists()) {
filename = url;
} else {
QDir d(basePath);
filename = d.relativeFilePath(url);
}
}
qApp->restoreOverrideCursor();
}
KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KisGroupLayer* layer;
layer = new KisGroupLayer(image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
// XXX: do something with filterversion?
Q_UNUSED(cs);
QString attr;
KisAdjustmentLayer* layer;
QString filtername;
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid adjustmentlayer! We should warn about it!
warnFile << "No filter in adjustment layer";
return 0;
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfiguration * kfc = f->defaultConfiguration(0);
// We'll load the configuration and the selection later.
layer = new KisAdjustmentLayer(image, name, kfc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KoShapeBasedDocumentBase * shapeController = 0;
if (m_d->document) {
shapeController = m_d->document->shapeController();
}
KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
// XXX: do something with generator version?
KisGeneratorLayer* layer;
QString generatorname = element.attribute(GENERATOR_NAME);
if (generatorname.isNull()) {
// XXX: Invalid generator layer! We should warn about it!
warnFile << "No generator in generator layer";
return 0;
}
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname);
if (!generator) {
warnFile << "No generator for generatorname" << generatorname << "";
return 0; // XXX: We don't have this generator. We should warn about it!
}
KisFilterConfiguration * kgc = generator->defaultConfiguration(0);
// We'll load the configuration and the selection later.
layer = new KisGeneratorLayer(image, name, kgc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageWSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity);
KisCloneInfo info;
if (! (element.attribute(CLONE_FROM_UUID)).isNull()) {
info = KisCloneInfo(QUuid(element.attribute(CLONE_FROM_UUID)));
} else {
if ((element.attribute(CLONE_FROM)).isNull()) {
return 0;
} else {
info = KisCloneInfo(element.attribute(CLONE_FROM));
}
}
layer->setCopyFromInfo(info);
if ((element.attribute(CLONE_TYPE)).isNull()) {
return 0;
} else {
layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt());
}
return layer;
}
KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element, KisNodeSP parent)
{
Q_UNUSED(parent);
QString attr;
KisFilterMask* mask;
QString filtername;
// XXX: should we check the version?
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid filter layer! We should warn about it!
warnFile << "No filter in filter layer";
return 0;
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfiguration * kfc = f->defaultConfiguration(0);
// We'll load the configuration and the selection later.
mask = new KisFilterMask();
mask->setFilter(kfc);
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element, KisNodeSP parent)
{
Q_UNUSED(element);
Q_UNUSED(parent);
KisTransformMask* mask;
/**
* We'll load the transform configuration later on a stage
* of binary data loading
*/
mask = new KisTransformMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element, KisNodeSP parent)
{
Q_UNUSED(element);
Q_UNUSED(parent);
KisTransparencyMask* mask = new KisTransparencyMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadSelectionMask(KisImageWSP image, const KoXmlElement& element, KisNodeSP parent)
{
Q_UNUSED(parent);
KisSelectionMaskSP mask = new KisSelectionMask(image);
bool active = element.attribute(ACTIVE, "1") == "0" ? false : true;
mask->setActive(active);
Q_CHECK_PTR(mask);
return mask;
}
void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageWSP image)
{
KoXmlNode child;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString name = e.attribute("name");
bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true;
KisLayerComposition* composition = new KisLayerComposition(image, name);
composition->setExportEnabled(exportEnabled);
KoXmlNode value;
for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) {
KoXmlElement e = value.toElement();
QUuid uuid(e.attribute("uuid"));
bool visible = e.attribute("visible", "1") == "0" ? false : true;
composition->setVisible(uuid, visible);
bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true;
composition->setCollapsed(uuid, collapsed);
}
image->addComposition(composition);
}
}
void KisKraLoader::loadAssistantsList(const KoXmlElement &elem)
{
KoXmlNode child;
int count = 0;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString type = e.attribute("type");
QString file_name = e.attribute("filename");
m_d->assistantsFilenames.insert(file_name,type);
count++;
}
}
diff --git a/krita/ui/kra/kis_kra_savexml_visitor.cpp b/krita/ui/kra/kis_kra_savexml_visitor.cpp
index 29736f7887b..1957d41d8bb 100644
--- a/krita/ui/kra/kis_kra_savexml_visitor.cpp
+++ b/krita/ui/kra/kis_kra_savexml_visitor.cpp
@@ -1,400 +1,400 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kra/kis_kra_savexml_visitor.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include <QTextStream>
#include <QDir>
#include <KoProperties.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <kis_debug.h>
#include <filter/kis_filter_configuration.h>
#include <generator/kis_generator_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_clone_layer.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_selection_mask.h>
#include <kis_shape_layer.h>
#include <kis_transparency_mask.h>
#include <kis_file_layer.h>
#include <kis_psd_layer_style.h>
using namespace KRA;
KisSaveXmlVisitor::KisSaveXmlVisitor(QDomDocument doc, const QDomElement & element, quint32 &count, const QString &url, bool root)
: KisNodeVisitor()
, m_doc(doc)
, m_count(count)
, m_url(url)
, m_root(root)
{
Q_ASSERT(!element.isNull());
m_elem = element;
}
void KisSaveXmlVisitor::setSelectedNodes(vKisNodeSP selectedNodes)
{
m_selectedNodes = selectedNodes;
}
QStringList KisSaveXmlVisitor::errorMessages() const
{
return m_errorMessages;
}
bool KisSaveXmlVisitor::visit(KisExternalLayer * layer)
{
if (layer->inherits("KisShapeLayer")) {
QDomElement layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, SHAPE_LAYER, layer);
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
else if (layer->inherits("KisFileLayer")) {
QDomElement layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, FILE_LAYER, layer);
KisFileLayer *fileLayer = dynamic_cast<KisFileLayer*>(layer);
QString path = fileLayer->path();
QDir d(QFileInfo(m_url).absolutePath());
layerElement.setAttribute("source", d.relativeFilePath(path));
if (fileLayer->scalingMethod() == KisFileLayer::ToImagePPI) {
layerElement.setAttribute("scale", "true");
}
else {
layerElement.setAttribute("scale", "false");
}
layerElement.setAttribute("scalingmethod", (int)fileLayer->scalingMethod());
layerElement.setAttribute(COLORSPACE_NAME, layer->original()->colorSpace()->id());
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
return false;
}
QDomElement KisSaveXmlVisitor::savePaintLayerAttributes(KisPaintLayer *layer, QDomDocument &doc)
{
QDomElement element = doc.createElement(LAYER);
saveLayer(element, PAINT_LAYER, layer);
element.setAttribute(CHANNEL_LOCK_FLAGS, flagsToString(layer->channelLockFlags()));
element.setAttribute(COLORSPACE_NAME, layer->paintDevice()->colorSpace()->id());
return element;
}
void KisSaveXmlVisitor::loadPaintLayerAttributes(const QDomElement &el, KisPaintLayer *layer)
{
loadLayerAttributes(el, layer);
if (el.hasAttribute(CHANNEL_LOCK_FLAGS)) {
layer->setChannelLockFlags(stringToFlags(el.attribute(CHANNEL_LOCK_FLAGS)));
}
}
bool KisSaveXmlVisitor::visit(KisPaintLayer *layer)
{
QDomElement layerElement = savePaintLayerAttributes(layer, m_doc);
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
bool KisSaveXmlVisitor::visit(KisGroupLayer *layer)
{
QDomElement layerElement;
if (m_root) // if this is the root we fake so not to save it
layerElement = m_elem;
else {
layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, GROUP_LAYER, layer);
layerElement.setAttribute(PASS_THROUGH_MODE, layer->passThroughMode());
m_elem.appendChild(layerElement);
}
QDomElement elem = m_doc.createElement(LAYERS);
Q_ASSERT(!layerElement.isNull());
layerElement.appendChild(elem);
KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
visitor.setSelectedNodes(m_selectedNodes);
m_count++;
bool success = visitor.visitAllInverse(layer);
m_errorMessages.append(visitor.errorMessages());
if (!m_errorMessages.isEmpty()) {
return false;
}
QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
while (i.hasNext()) {
i.next();
m_nodeFileNames[i.key()] = i.value();
}
return success;
}
bool KisSaveXmlVisitor::visit(KisAdjustmentLayer* layer)
{
if (!layer->filter()) {
return false;
}
QDomElement layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, ADJUSTMENT_LAYER, layer);
layerElement.setAttribute(FILTER_NAME, layer->filter()->name());
layerElement.setAttribute(FILTER_VERSION, layer->filter()->version());
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
bool KisSaveXmlVisitor::visit(KisGeneratorLayer *layer)
{
QDomElement layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, GENERATOR_LAYER, layer);
layerElement.setAttribute(GENERATOR_NAME, layer->filter()->name());
layerElement.setAttribute(GENERATOR_VERSION, layer->filter()->version());
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
bool KisSaveXmlVisitor::visit(KisCloneLayer *layer)
{
QDomElement layerElement = m_doc.createElement(LAYER);
saveLayer(layerElement, CLONE_LAYER, layer);
layerElement.setAttribute(CLONE_FROM, layer->copyFromInfo().name());
layerElement.setAttribute(CLONE_FROM_UUID, layer->copyFromInfo().uuid().toString());
layerElement.setAttribute(CLONE_TYPE, layer->copyType());
m_elem.appendChild(layerElement);
m_count++;
return saveMasks(layer, layerElement);
}
bool KisSaveXmlVisitor::visit(KisFilterMask *mask)
{
Q_ASSERT(mask);
if (!mask->filter()) {
return false;
}
QDomElement el = m_doc.createElement(MASK);
saveMask(el, FILTER_MASK, mask);
el.setAttribute(FILTER_NAME, mask->filter()->name());
el.setAttribute(FILTER_VERSION, mask->filter()->version());
m_elem.appendChild(el);
m_count++;
return true;
}
bool KisSaveXmlVisitor::visit(KisTransformMask *mask)
{
Q_ASSERT(mask);
QDomElement el = m_doc.createElement(MASK);
saveMask(el, TRANSFORM_MASK, mask);
m_elem.appendChild(el);
m_count++;
return true;
}
bool KisSaveXmlVisitor::visit(KisTransparencyMask *mask)
{
Q_ASSERT(mask);
QDomElement el = m_doc.createElement(MASK);
saveMask(el, TRANSPARENCY_MASK, mask);
m_elem.appendChild(el);
m_count++;
return true;
}
bool KisSaveXmlVisitor::visit(KisSelectionMask *mask)
{
Q_ASSERT(mask);
QDomElement el = m_doc.createElement(MASK);
saveMask(el, SELECTION_MASK, mask);
m_elem.appendChild(el);
m_count++;
return true;
}
void KisSaveXmlVisitor::loadLayerAttributes(const QDomElement &el, KisLayer *layer)
{
if (el.hasAttribute(NAME)) {
QString layerName = el.attribute(NAME);
KIS_ASSERT_RECOVER_RETURN(layerName == layer->name());
}
if (el.hasAttribute(CHANNEL_FLAGS)) {
layer->setChannelFlags(stringToFlags(el.attribute(CHANNEL_FLAGS)));
}
if (el.hasAttribute(OPACITY)) {
layer->setOpacity(el.attribute(OPACITY).toInt());
}
if (el.hasAttribute(COMPOSITE_OP)) {
layer->setCompositeOp(el.attribute(COMPOSITE_OP));
}
if (el.hasAttribute(VISIBLE)) {
layer->setVisible(el.attribute(VISIBLE).toInt());
}
if (el.hasAttribute(LOCKED)) {
layer->setUserLocked(el.attribute(LOCKED).toInt());
}
if (el.hasAttribute(X)) {
layer->setX(el.attribute(X).toInt());
}
if (el.hasAttribute(Y)) {
layer->setY(el.attribute(Y).toInt());
}
if (el.hasAttribute(UUID)) {
layer->setUuid(el.attribute(UUID));
}
if (el.hasAttribute(COLLAPSED)) {
layer->setCollapsed(el.attribute(COLLAPSED).toInt());
}
if (el.hasAttribute(LAYER_STYLE_UUID)) {
QString uuidString = el.attribute(LAYER_STYLE_UUID);
QUuid uuid(uuidString);
if (!uuid.isNull()) {
KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
dumbLayerStyle->setUuid(uuid);
layer->setLayerStyle(dumbLayerStyle);
} else {
- qWarning() << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
+ warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
}
}
}
void KisSaveXmlVisitor::saveLayer(QDomElement & el, const QString & layerType, const KisLayer * layer)
{
el.setAttribute(CHANNEL_FLAGS, flagsToString(layer->channelFlags()));
el.setAttribute(NAME, layer->name());
el.setAttribute(OPACITY, layer->opacity());
el.setAttribute(COMPOSITE_OP, layer->compositeOp()->id());
el.setAttribute(VISIBLE, layer->visible());
el.setAttribute(LOCKED, layer->userLocked());
el.setAttribute(NODE_TYPE, layerType);
el.setAttribute(FILE_NAME, LAYER + QString::number(m_count));
el.setAttribute(X, layer->x());
el.setAttribute(Y, layer->y());
el.setAttribute(UUID, layer->uuid().toString());
el.setAttribute(COLLAPSED, layer->collapsed());
if (layer->layerStyle()) {
el.setAttribute(LAYER_STYLE_UUID, layer->layerStyle()->uuid().toString());
}
foreach (KisNodeSP node, m_selectedNodes) {
if (node.data() == layer) {
el.setAttribute("selected", "true");
break;
}
}
m_nodeFileNames[layer] = LAYER + QString::number(m_count);
dbgFile << "Saved layer "
<< layer->name()
<< " of type " << layerType
<< " with filename " << LAYER + QString::number(m_count);
}
void KisSaveXmlVisitor::saveMask(QDomElement & el, const QString & maskType, const KisMask * mask)
{
el.setAttribute(NAME, mask->name());
el.setAttribute(VISIBLE, mask->visible());
el.setAttribute(LOCKED, mask->userLocked());
el.setAttribute(NODE_TYPE, maskType);
el.setAttribute(FILE_NAME, MASK + QString::number(m_count));
el.setAttribute(X, mask->x());
el.setAttribute(Y, mask->y());
el.setAttribute(UUID, mask->uuid().toString());
if (maskType == SELECTION_MASK) {
el.setAttribute(ACTIVE, mask->nodeProperties().boolProperty("active"));
}
m_nodeFileNames[mask] = MASK + QString::number(m_count);
dbgFile << "Saved mask "
<< mask->name()
<< " of type " << maskType
<< " with filename " << MASK + QString::number(m_count);
}
bool KisSaveXmlVisitor::saveMasks(KisNode * node, QDomElement & layerElement)
{
if (node->childCount() > 0) {
QDomElement elem = m_doc.createElement(MASKS);
Q_ASSERT(!layerElement.isNull());
layerElement.appendChild(elem);
KisSaveXmlVisitor visitor(m_doc, elem, m_count, m_url, false);
visitor.setSelectedNodes(m_selectedNodes);
bool success = visitor.visitAllInverse(node);
m_errorMessages.append(visitor.errorMessages());
if (!m_errorMessages.isEmpty()) {
return false;
}
QMapIterator<const KisNode*, QString> i(visitor.nodeFileNames());
while (i.hasNext()) {
i.next();
m_nodeFileNames[i.key()] = i.value();
}
return success;
}
return true;
}
diff --git a/krita/ui/opengl/kis_opengl_canvas2.cpp b/krita/ui/opengl/kis_opengl_canvas2.cpp
index 9d7b00804bf..03984a8677e 100644
--- a/krita/ui/opengl/kis_opengl_canvas2.cpp
+++ b/krita/ui/opengl/kis_opengl_canvas2.cpp
@@ -1,779 +1,779 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006-2013
* Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define GL_GLEXT_PROTOTYPES
#include "opengl/kis_opengl_canvas2.h"
#ifdef HAVE_OPENGL
#include <QFile>
#include <QMenu>
#include <QWidget>
#include <QBrush>
#include <QPainter>
#include <QPaintEvent>
#include <QPoint>
#include <QPainter>
#include <QMatrix>
-#include <QDebug>
+#include <kis_debug.h>
#include <QThread>
#include <QMessageBox>
#include <QFile>
#include <QOpenGLShaderProgram>
#include <QTransform>
#include <QOpenGLShaderProgram>
#include <QOpenGLFramebufferObject>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <kstandarddirs.h>
#include <kglobal.h>
#include "KoToolProxy.h"
#include "kis_config.h"
#include "kis_types.h"
#include <kis_favorite_resource_manager.h>
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "kis_image.h"
#include "opengl/kis_opengl_image_textures.h"
#include "kis_canvas_resource_provider.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_debug.h"
#include "opengl/kis_opengl_canvas2_p.h"
#include "kis_coordinates_converter.h"
#include "canvas/kis_display_filter.h"
#include "canvas/kis_display_color_converter.h"
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
static bool OPENGL_SUCCESS = false;
namespace
{
const GLuint NO_PROGRAM = 0;
}
struct KisOpenGLCanvas2::Private
{
public:
Private()
: canvasInitialized(false)
, displayShader(0)
, checkerShader(0)
, glSyncObject(0)
, wrapAroundMode(false)
{
vertices = new QVector3D[6];
texCoords = new QVector2D[6];
}
~Private() {
delete displayShader;
delete checkerShader;
delete[] vertices;
delete[] texCoords;
Sync::deleteSync(glSyncObject);
}
bool canvasInitialized;
QVector3D *vertices;
QVector2D *texCoords;
KisOpenGLImageTexturesSP openGLImageTextures;
QOpenGLShaderProgram *displayShader;
int displayUniformLocationModelViewProjection;
int displayUniformLocationTextureMatrix;
int displayUniformLocationViewPortScale;
int displayUniformLocationTexelSize;
int displayUniformLocationTexture0;
int displayUniformLocationTexture1;
QOpenGLShaderProgram *checkerShader;
int checkerUniformLocationModelViewProjection;
int checkerUniformLocationTextureMatrix;
KisDisplayFilter* displayFilter;
KisTextureTile::FilterMode filterMode;
GLsync glSyncObject;
bool firstDrawImage;
qreal scaleX, scaleY;
bool wrapAroundMode;
int xToColWithWrapCompensation(int x, const QRect &imageRect) {
int firstImageColumn = openGLImageTextures->xToCol(imageRect.left());
int lastImageColumn = openGLImageTextures->xToCol(imageRect.right());
int colsPerImage = lastImageColumn - firstImageColumn + 1;
int numWraps = floor(qreal(x) / imageRect.width());
int remainder = x - imageRect.width() * numWraps;
return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder);
}
int yToRowWithWrapCompensation(int y, const QRect &imageRect) {
int firstImageRow = openGLImageTextures->yToRow(imageRect.top());
int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom());
int rowsPerImage = lastImageRow - firstImageRow + 1;
int numWraps = floor(qreal(y) / imageRect.height());
int remainder = y - imageRect.height() * numWraps;
return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder);
}
};
KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter)
: QOpenGLWidget(parent)
, KisCanvasWidgetBase(canvas, coordinatesConverter)
, d(new Private())
{
QSurfaceFormat format;
format.setDepthBufferSize(24);
setFormat(format);
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_STARTED");
d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(image,
colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
setAcceptDrops(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_AcceptTouchEvents);
setAutoFillBackground(false);
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setDisplayFilter(colorConverter->displayFilter());
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
}
KisOpenGLCanvas2::~KisOpenGLCanvas2()
{
delete d;
}
void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter)
{
bool needsInternalColorManagement =
!displayFilter || displayFilter->useInternalColorManagement();
bool needsFullRefresh =
d->openGLImageTextures->
setInternalColorManagementActive(needsInternalColorManagement);
d->displayFilter = displayFilter;
if (d->canvasInitialized) {
d->canvasInitialized = false;
initializeDisplayShader();
initializeCheckerShader();
d->canvasInitialized = true;
}
if (needsFullRefresh) {
canvas()->startUpdateInPatches(canvas()->image()->bounds());
} else {
canvas()->updateCanvas();
}
}
void KisOpenGLCanvas2::disconnectCurrentCanvas()
{
Q_ASSERT(d->openGLImageTextures);
d->openGLImageTextures->disconnect(canvas());
d->openGLImageTextures->disconnect(canvas()->image());
}
void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value)
{
d->wrapAroundMode = value;
update();
}
void KisOpenGLCanvas2::initializeGL()
{
// KisConfig cfg;
// if (cfg.disableVSync()) {
// if (!VSyncWorkaround::tryDisableVSync(this)) {
-// qWarning();
-// qWarning() << "WARNING: We didn't manage to switch off VSync on your graphics adapter.";
-// qWarning() << "WARNING: It means either your hardware or driver doesn't support it,";
-// qWarning() << "WARNING: or we just don't know about this hardware. Please report us a bug";
-// qWarning() << "WARNING: with the output of \'glxinfo\' for your card.";
-// qWarning();
-// qWarning() << "WARNING: Trying to workaround it by disabling Double Buffering.";
-// qWarning() << "WARNING: You may see some flickering when painting with some tools. It doesn't";
-// qWarning() << "WARNING: affect the quality of the final image, though.";
-// qWarning();
+// warnKrita;
+// warnKrita << "WARNING: We didn't manage to switch off VSync on your graphics adapter.";
+// warnKrita << "WARNING: It means either your hardware or driver doesn't support it,";
+// warnKrita << "WARNING: or we just don't know about this hardware. Please report us a bug";
+// warnKrita << "WARNING: with the output of \'glxinfo\' for your card.";
+// warnKrita;
+// warnKrita << "WARNING: Trying to workaround it by disabling Double Buffering.";
+// warnKrita << "WARNING: You may see some flickering when painting with some tools. It doesn't";
+// warnKrita << "WARNING: affect the quality of the final image, though.";
+// warnKrita;
// if (cfg.disableDoubleBuffering() && QOpenGLContext::currentContext()->format().swapBehavior() == QSurfaceFormat::DoubleBuffer) {
-// qCritical() << "CRITICAL: Failed to disable Double Buffering. Lines may look \"bended\" on your image.";
-// qCritical() << "CRITICAL: Your graphics card or driver does not fully support Krita's OpenGL canvas.";
-// qCritical() << "CRITICAL: For an optimal experience, please disable OpenGL";
-// qCritical();
+// errKrita << "CRITICAL: Failed to disable Double Buffering. Lines may look \"bended\" on your image.";
+// errKrita << "CRITICAL: Your graphics card or driver does not fully support Krita's OpenGL canvas.";
+// errKrita << "CRITICAL: For an optimal experience, please disable OpenGL";
+// errKrita;
// }
// }
// }
KisConfig cfg;
- qDebug() << "OpenGL: Preparing to initialize OpenGL for KisCanvas";
+ dbgKrita << "OpenGL: Preparing to initialize OpenGL for KisCanvas";
int glVersion = KisOpenGL::initializeContext(context());
- qDebug() << "OpenGL: Version found" << glVersion;
+ dbgKrita << "OpenGL: Version found" << glVersion;
initializeOpenGLFunctions();
VSyncWorkaround::tryDisableVSync(context());
d->openGLImageTextures->initGL((QOpenGLFunctions *)this);
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
initializeCheckerShader();
initializeDisplayShader();
Sync::init();
/**
* Warn about Intel's broken video drivers
*/
#if defined HAVE_OPENGL && defined Q_OS_WIN
#ifndef GL_RENDERER
# define GL_RENDERER 0x1F01
#endif
QString renderer = QString((const char*)glGetString(GL_RENDERER));
if (cfg.useOpenGL() && renderer.startsWith("Intel") && !cfg.readEntry("WarnedAboutIntel", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You have an Intel(R) HD Graphics video adapter.\n"
"If you experience problems like a black or blank screen,"
"please update your display driver to the latest version.\n\n"
"You can also disable OpenGL rendering in Krita's Settings.\n"));
cfg.writeEntry("WarnedAboutIntel", true);
}
#endif
d->canvasInitialized = true;
}
void KisOpenGLCanvas2::resizeGL(int width, int height)
{
coordinatesConverter()->setCanvasWidgetSize(QSize(width, height));
paintGL();
}
void KisOpenGLCanvas2::paintGL()
{
if (!OPENGL_SUCCESS) {
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED");
}
QPainter gc(this);
gc.beginNativePainting();
renderCanvasGL();
if (d->glSyncObject) {
Sync::deleteSync(d->glSyncObject);
}
d->glSyncObject = Sync::getSync();
gc.endNativePainting();
renderDecorations(&gc);
gc.end();
if (!OPENGL_SUCCESS) {
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
OPENGL_SUCCESS = true;
}
}
bool KisOpenGLCanvas2::isBusy() const
{
return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled;
}
inline void rectToVertices(QVector3D* vertices, const QRectF &rc)
{
vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f);
vertices[1] = QVector3D(rc.left(), rc.top(), 0.f);
vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f);
vertices[3] = QVector3D(rc.left(), rc.top(), 0.f);
vertices[4] = QVector3D(rc.right(), rc.top(), 0.f);
vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f);
}
inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc)
{
texCoords[0] = QVector2D(rc.left(), rc.bottom());
texCoords[1] = QVector2D(rc.left(), rc.top());
texCoords[2] = QVector2D(rc.right(), rc.bottom());
texCoords[3] = QVector2D(rc.left(), rc.top());
texCoords[4] = QVector2D(rc.right(), rc.top());
texCoords[5] = QVector2D(rc.right(), rc.bottom());
}
void KisOpenGLCanvas2::drawCheckers()
{
if (!d->checkerShader) {
return;
}
KisCoordinatesConverter *converter = coordinatesConverter();
QTransform textureTransform;
QTransform modelTransform;
QRectF textureRect;
QRectF modelRect;
QRectF viewportRect = !d->wrapAroundMode ?
converter->imageRectInViewportPixels() :
converter->widgetToViewport(this->rect());
converter->getOpenGLCheckersInfo(viewportRect,
&textureTransform, &modelTransform, &textureRect, &modelRect);
// XXX: getting a config object every time we draw the checkers is bad for performance!
KisConfig cfg;
GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast<GLfloat>(cfg.checkSize());
textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
d->checkerShader->bind();
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(modelTransform);
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->checkerShader->setUniformValue(d->checkerUniformLocationModelViewProjection, modelMatrix);
QMatrix4x4 textureMatrix(textureTransform);
d->checkerShader->setUniformValue(d->checkerUniformLocationTextureMatrix, textureMatrix);
//Setup the geometry for rendering
rectToVertices(d->vertices, modelRect);
d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices);
rectToTexCoords(d->texCoords, textureRect);
d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords);
// render checkers
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture());
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindTexture(GL_TEXTURE_2D, 0);
d->checkerShader->release();
}
void KisOpenGLCanvas2::drawImage()
{
if (!d->displayShader) {
return;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
KisCoordinatesConverter *converter = coordinatesConverter();
d->displayShader->bind();
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->displayShader->setUniformValue(d->displayUniformLocationModelViewProjection, modelMatrix);
QMatrix4x4 textureMatrix;
textureMatrix.setToIdentity();
d->displayShader->setUniformValue(d->displayUniformLocationTextureMatrix, textureMatrix);
QRectF widgetRect(0,0, width(), height());
QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect));
qreal scaleX, scaleY;
converter->imageScale(&scaleX, &scaleY);
d->displayShader->setUniformValue(d->displayUniformLocationViewPortScale, (GLfloat) scaleX);
d->displayShader->setUniformValue(d->displayUniformLocationTexelSize, (GLfloat) d->openGLImageTextures->texelSize());
QRect ir = d->openGLImageTextures->storedImageBounds();
QRect wr = widgetRectInImagePixels.toAlignedRect();
if (!d->wrapAroundMode) {
// if we don't want to paint wrapping images, just limit the
// processing area, and the code will handle all the rest
wr &= ir;
}
int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir);
int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir);
int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir);
int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir);
int minColumn = d->openGLImageTextures->xToCol(ir.left());
int maxColumn = d->openGLImageTextures->xToCol(ir.right());
int minRow = d->openGLImageTextures->yToRow(ir.top());
int maxRow = d->openGLImageTextures->yToRow(ir.bottom());
int imageColumns = maxColumn - minColumn + 1;
int imageRows = maxRow - minRow + 1;
for (int col = firstColumn; col <= lastColumn; col++) {
for (int row = firstRow; row <= lastRow; row++) {
int effectiveCol = col;
int effectiveRow = row;
QPointF tileWrappingTranslation;
if (effectiveCol > maxColumn || effectiveCol < minColumn) {
int translationStep = floor(qreal(col) / imageColumns);
int originCol = translationStep * imageColumns;
effectiveCol = col - originCol;
tileWrappingTranslation.rx() = translationStep * ir.width();
}
if (effectiveRow > maxRow || effectiveRow < minRow) {
int translationStep = floor(qreal(row) / imageRows);
int originRow = translationStep * imageRows;
effectiveRow = row - originRow;
tileWrappingTranslation.ry() = translationStep * ir.height();
}
KisTextureTile *tile =
d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow);
if (!tile) {
- qWarning() << "OpenGL: Trying to paint texture tile but it has not been created yet.";
+ warnKrita << "OpenGL: Trying to paint texture tile but it has not been created yet.";
continue;
}
/*
* We create a float rect here to workaround Qt's
* "history reasons" in calculation of right()
* and bottom() coordinates of integer rects.
*/
QRectF textureRect(tile->tileRectInTexturePixels());
QRectF modelRect(tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y()));
//Setup the geometry for rendering
rectToVertices(d->vertices, modelRect);
d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices);
rectToTexCoords(d->texCoords, textureRect);
d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords);
if (d->displayFilter) {
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture());
d->displayShader->setUniformValue(d->displayUniformLocationTexture1, 1);
}
glActiveTexture(GL_TEXTURE0);
tile->bindToActiveTexture();
if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
switch(d->filterMode) {
case KisTextureTile::NearestFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case KisTextureTile::BilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case KisTextureTile::TrilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case KisTextureTile::HighQualityFiltering:
if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
break;
}
}
glDrawArrays(GL_TRIANGLES, 0, 6);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
d->displayShader->release();
}
void KisOpenGLCanvas2::reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log)
{
KisConfig cfg;
if (cfg.useVerboseOpenGLDebugOutput()) {
- qDebug() << "GL-log:" << context << log;
+ dbgKrita << "GL-log:" << context << log;
}
if (result) return;
QMessageBox::critical(this, i18nc("@title:window", "Krita"),
QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n%2\n\n Krita will disable OpenGL and close now.")).arg(context).arg(log),
QMessageBox::Close);
cfg.setUseOpenGL(false);
cfg.setCanvasState("OPENGL_FAILED");
}
void KisOpenGLCanvas2::initializeCheckerShader()
{
if (d->canvasInitialized) return;
delete d->checkerShader;
d->checkerShader = new QOpenGLShaderProgram();
QString vertexShaderName;
QString fragmentShaderName;
if (KisOpenGL::supportsGLSL13()) {
vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert");
fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture.frag");
} else {
vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert");
fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture_legacy.frag");
}
bool result;
result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderName);
reportShaderLinkFailedAndExit(result, "Checker vertex shader", d->checkerShader->log());
result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderName);
reportShaderLinkFailedAndExit(result, "Checker fragment shader", d->checkerShader->log());
d->checkerShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
d->checkerShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE);
result = d->checkerShader->link();
reportShaderLinkFailedAndExit(result, "Checker shader (link)", d->checkerShader->log());
Q_ASSERT(d->checkerShader->isLinked());
d->checkerUniformLocationModelViewProjection = d->checkerShader->uniformLocation("modelViewProjection");
d->checkerUniformLocationTextureMatrix = d->checkerShader->uniformLocation("textureMatrix");
}
QByteArray KisOpenGLCanvas2::buildFragmentShader()
{
QByteArray shaderText;
bool haveDisplayFilter = d->displayFilter && !d->displayFilter->program().isEmpty();
bool useHiQualityFiltering = d->filterMode == KisTextureTile::HighQualityFiltering;
bool haveGLSL13 = KisOpenGL::supportsGLSL13();
QString filename = haveGLSL13 && useHiQualityFiltering ?
"highq_downscale" : "simple_texture";
QString legacyPostfix = !haveGLSL13 ? "_legacy" : "";
QString filterPostfix = haveDisplayFilter ? "_ocio" : "";
QString prefaceKey = QString("krita/shaders/%1%2_preface.frag.inc")
.arg(filename)
.arg(legacyPostfix);
QString mainKey = QString("krita/shaders/%1%2_main%3.frag.inc")
.arg(filename)
.arg(legacyPostfix)
.arg(filterPostfix);
{
QFile prefaceFile(KGlobal::dirs()->findResource("data", prefaceKey));
prefaceFile.open(QIODevice::ReadOnly);
shaderText.append(prefaceFile.readAll());
}
if (haveDisplayFilter) {
shaderText.append(d->displayFilter->program().toLatin1());
}
{
QFile mainFile(KGlobal::dirs()->findResource("data", mainKey));
mainFile.open(QIODevice::ReadOnly);
shaderText.append(mainFile.readAll());
}
return shaderText;
}
void KisOpenGLCanvas2::initializeDisplayShader()
{
if (d->canvasInitialized) return;
delete d->displayShader;
d->displayShader = new QOpenGLShaderProgram();
bool result = d->displayShader->addShaderFromSourceCode(QOpenGLShader::Fragment, buildFragmentShader());
reportShaderLinkFailedAndExit(result, "Display fragment shader", d->displayShader->log());
if (KisOpenGL::supportsGLSL13()) {
result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert"));
} else {
result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert"));
}
reportShaderLinkFailedAndExit(result, "Display vertex shader", d->displayShader->log());
d->displayShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
d->displayShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE);
result = d->displayShader->link();
reportShaderLinkFailedAndExit(result, "Display shader (link)", d->displayShader->log());
Q_ASSERT(d->displayShader->isLinked());
d->displayUniformLocationModelViewProjection = d->displayShader->uniformLocation("modelViewProjection");
d->displayUniformLocationTextureMatrix = d->displayShader->uniformLocation("textureMatrix");
d->displayUniformLocationViewPortScale = d->displayShader->uniformLocation("viewportScale");
d->displayUniformLocationTexelSize = d->displayShader->uniformLocation("texelSize");
d->displayUniformLocationTexture0 = d->displayShader->uniformLocation("texture0");
d->displayUniformLocationTexture1 = d->displayShader->uniformLocation("texture1");
}
void KisOpenGLCanvas2::slotConfigChanged()
{
KisConfig cfg;
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels());
d->filterMode = (KisTextureTile::FilterMode) cfg.openGLFilteringMode();
notifyConfigChanged();
}
QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const
{
return processInputMethodQuery(query);
}
void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event)
{
processInputMethodEvent(event);
}
void KisOpenGLCanvas2::renderCanvasGL()
{
// Draw the border (that is, clear the whole widget to the border color)
QColor widgetBackgroundColor = borderColor();
glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0);
glClear(GL_COLOR_BUFFER_BIT);
drawCheckers();
drawImage();
}
void KisOpenGLCanvas2::renderDecorations(QPainter *painter)
{
QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect();
drawDecorations(*painter, boundingRect);
}
void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisOpenGLCanvas2::channelSelectionChanged(QBitArray channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
}
void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h)
{
d->openGLImageTextures->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
return d->openGLImageTextures->updateCache(rc);
}
QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
{
// See KisQPainterCanvas::updateCanvasProjection for more info
bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
if (isOpenGLUpdateInfo) {
d->openGLImageTextures->recalculateCache(info);
}
return QRect(); // FIXME: Implement dirty rect for OpenGL
}
bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
#endif // HAVE_OPENGL
diff --git a/krita/ui/opengl/kis_opengl_canvas2_p.h b/krita/ui/opengl/kis_opengl_canvas2_p.h
index a28c40fe5aa..d8ffcc748bb 100644
--- a/krita/ui/opengl/kis_opengl_canvas2_p.h
+++ b/krita/ui/opengl/kis_opengl_canvas2_p.h
@@ -1,253 +1,253 @@
/*
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_OPENGL_CANVAS_2_P_H
#define KIS_OPENGL_CANVAS_2_P_H
#include <opengl/kis_opengl.h>
#ifdef HAVE_OPENGL
/**
* This is a workaround for a very slow updates in OpenGL canvas (~6ms).
* The delay happens because of VSync in the swapBuffers() call. At first
* we try to disable VSync. If it fails we just disable Double Buffer
* completely.
*
* This file is effectively a bit of copy-paste from qgl_x11.cpp
*/
#if defined Q_OS_LINUX
#include <QByteArray>
#include <QVector>
#include <QLibrary>
#include <QX11Info>
#include <QOpenGLContext>
#include <QApplication>
#ifndef GL_NUM_EXTENSIONS
#define GL_NUM_EXTENSIONS 0x821D
#endif
QString gl_library_name() {
#if defined (QT_OPENGL_ES_2)
return QLatin1String("GLESv2");
#else
return QLatin1String("GL");
#endif
}
namespace VSyncWorkaround {
bool tryDisableVSync(QOpenGLContext* ctx) {
bool result = false;
bool triedDisable = false;
Display *dpy = QX11Info::display();
- qDebug() << "OpenGL architecture is" << gl_library_name();
+ dbgKrita << "OpenGL architecture is" << gl_library_name();
if (ctx->hasExtension("GLX_EXT_swap_control")) {
- qDebug() << "Swap control extension found.";
+ dbgKrita << "Swap control extension found.";
typedef WId (*k_glXGetCurrentDrawable)(void);
typedef void (*kis_glXSwapIntervalEXT)(Display*, WId, int);
k_glXGetCurrentDrawable kis_glXGetCurrentDrawable = (k_glXGetCurrentDrawable)ctx->getProcAddress("glXGetCurrentDrawable");
kis_glXSwapIntervalEXT glXSwapIntervalEXT = (kis_glXSwapIntervalEXT)ctx->getProcAddress("glXSwapIntervalEXT");
WId wid = kis_glXGetCurrentDrawable();
if (glXSwapIntervalEXT) {
glXSwapIntervalEXT(dpy, wid, 0);
triedDisable = true;
unsigned int swap = 1;
#ifdef GLX_SWAP_INTERVAL_EXT
typedef int (*k_glXQueryDrawable)(Display *, WId, int, unsigned int *);
k_glXQueryDrawable kis_glXQueryDrawable = (k_glXQueryDrawable)ctx->getProcAddress("glXQueryDrawable");
kis_glXQueryDrawable(dpy, wid, GLX_SWAP_INTERVAL_EXT, &swap);
#endif
result = !swap;
} else {
- qDebug() << "Couldn't load glXSwapIntervalEXT extension function";
+ dbgKrita << "Couldn't load glXSwapIntervalEXT extension function";
}
} else if (ctx->hasExtension("GLX_MESA_swap_control")) {
- qDebug() << "MESA swap control extension found.";
+ dbgKrita << "MESA swap control extension found.";
typedef int (*kis_glXSwapIntervalMESA)(unsigned int);
typedef int (*kis_glXGetSwapIntervalMESA)(void);
kis_glXSwapIntervalMESA glXSwapIntervalMESA = (kis_glXSwapIntervalMESA)ctx->getProcAddress("glXSwapIntervalMESA");
kis_glXGetSwapIntervalMESA glXGetSwapIntervalMESA = (kis_glXGetSwapIntervalMESA)ctx->getProcAddress("glXGetSwapIntervalMESA");
if (glXSwapIntervalMESA) {
int retval = glXSwapIntervalMESA(0);
triedDisable = true;
int swap = 1;
if (glXGetSwapIntervalMESA) {
swap = glXGetSwapIntervalMESA();
} else {
- qDebug() << "Couldn't load glXGetSwapIntervalMESA extension function";
+ dbgKrita << "Couldn't load glXGetSwapIntervalMESA extension function";
}
result = !retval && !swap;
} else {
- qDebug() << "Couldn't load glXSwapIntervalMESA extension function";
+ dbgKrita << "Couldn't load glXSwapIntervalMESA extension function";
}
} else {
- qDebug() << "There is neither GLX_EXT_swap_control or GLX_MESA_swap_control extension supported";
+ dbgKrita << "There is neither GLX_EXT_swap_control or GLX_MESA_swap_control extension supported";
}
if (triedDisable && !result) {
- qCritical();
- qCritical() << "CRITICAL: Your video driver forbids disabling VSync!";
- qCritical() << "CRITICAL: Try toggling some VSync- or VBlank-related options in your driver configuration dialog.";
- qCritical() << "CRITICAL: NVIDIA users can do:";
- qCritical() << "CRITICAL: sudo nvidia-settings > (tab) OpenGL settings > Sync to VBlank ( unchecked )";
- qCritical();
+ errKrita;
+ errKrita << "CRITICAL: Your video driver forbids disabling VSync!";
+ errKrita << "CRITICAL: Try toggling some VSync- or VBlank-related options in your driver configuration dialog.";
+ errKrita << "CRITICAL: NVIDIA users can do:";
+ errKrita << "CRITICAL: sudo nvidia-settings > (tab) OpenGL settings > Sync to VBlank ( unchecked )";
+ errKrita;
}
return result;
}
}
#elif defined Q_OS_WIN
namespace VSyncWorkaround {
bool tryDisableVSync(QOpenGLContext *ctx) {
bool retval = false;
if (ctx->hasExtension("WGL_EXT_swap_control")) {
typedef void (*wglSwapIntervalEXT)(int);
typedef int (*wglGetSwapIntervalEXT)(void);
((wglSwapIntervalEXT)ctx->getProcAddress("wglSwapIntervalEXT"))(0);
int interval = ((wglGetSwapIntervalEXT)ctx->getProcAddress("wglGetSwapIntervalEXT"))();
if (interval) {
- qWarning() << "Failed to disable VSync with WGL_EXT_swap_control";
+ warnKrita << "Failed to disable VSync with WGL_EXT_swap_control";
}
retval = !interval;
} else {
- qWarning() << "WGL_EXT_swap_control extension is not available. Found extensions" << ctx->extensions();
+ warnKrita << "WGL_EXT_swap_control extension is not available. Found extensions" << ctx->extensions();
}
return retval;
}
}
#else // !defined Q_OS_LINUX && !defined Q_OS_WIN
namespace VSyncWorkaround {
bool tryDisableVSync(QOpenGLContext *) {
return false;
}
}
#endif // defined Q_OS_LINUX
namespace Sync {
//For checking sync status
enum SyncStatus {
Signaled,
Unsignaled
};
#ifndef GL_SYNC_GPU_COMMANDS_COMPLETE
#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
#endif
#ifndef GL_UNSIGNALED
#define GL_UNSIGNALED 0x9118
#endif
#ifndef GL_SIGNALED
#define GL_SIGNALED 0x9119
#endif
#ifndef GL_SYNC_STATUS
#define GL_SYNC_STATUS 0x9114
#endif
//Function pointers for glFenceSync and glGetSynciv
typedef GLsync (*kis_glFenceSync)(GLenum, GLbitfield);
static kis_glFenceSync k_glFenceSync = 0;
typedef void (*kis_glGetSynciv)(GLsync, GLenum, GLsizei, GLsizei*, GLint*);
static kis_glGetSynciv k_glGetSynciv = 0;
typedef void (*kis_glDeleteSync)(GLsync);
static kis_glDeleteSync k_glDeleteSync = 0;
typedef GLenum (*kis_glClientWaitSync)(GLsync,GLbitfield,GLuint64);
static kis_glClientWaitSync k_glClientWaitSync = 0;
//Initialise the function pointers for glFenceSync and glGetSynciv
//Note: Assumes a current OpenGL context.
void init() {
QOpenGLContext* ctx = QOpenGLContext::currentContext();
#if defined Q_OS_WIN
if (KisOpenGL::supportsFenceSync()) {
#ifdef ENV64BIT
k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync");
k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv");
k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync");
#else
k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSyncARB");
k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSyncivARB");
k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSyncARB");
#endif
k_glClientWaitSync = (kis_glClientWaitSync)ctx->getProcAddress("glClientWaitSync");
}
#elif defined Q_OS_LINUX
if (KisOpenGL::supportsFenceSync()) {
k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync");
k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv");
k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync");
k_glClientWaitSync = (kis_glClientWaitSync)ctx->getProcAddress("glClientWaitSync");
}
#endif
if (k_glFenceSync == 0 || k_glGetSynciv == 0 ||
k_glDeleteSync == 0 || k_glClientWaitSync == 0) {
qWarning("Could not find sync functions, disabling sync notification.");
}
}
//Get a fence sync object from OpenGL
GLsync getSync() {
if(k_glFenceSync) {
GLsync sync = k_glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (KisOpenGL::needsFenceWorkaround()) {
k_glClientWaitSync(sync, 0, 1);
}
return sync;
}
return 0;
}
//Check the status of a sync object
SyncStatus syncStatus(GLsync syncObject) {
if(syncObject && k_glGetSynciv) {
GLint status = -1;
k_glGetSynciv(syncObject, GL_SYNC_STATUS, 1, 0, &status);
return status == GL_SIGNALED ? Sync::Signaled : Sync::Unsignaled;
}
return Sync::Signaled;
}
void deleteSync(GLsync syncObject) {
if(syncObject && k_glDeleteSync) {
k_glDeleteSync(syncObject);
}
}
}
#endif // HAVE_OPENGL
#endif // KIS_OPENGL_CANVAS_2_P_H
diff --git a/krita/ui/opengl/kis_opengl_image_textures.cpp b/krita/ui/opengl/kis_opengl_image_textures.cpp
index ce6794c2301..9c2b22cd527 100644
--- a/krita/ui/opengl/kis_opengl_image_textures.cpp
+++ b/krita/ui/opengl/kis_opengl_image_textures.cpp
@@ -1,566 +1,566 @@
/*
* Copyright (c) 2005-2007 Adrian Page <adrian@pagenet.plus.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "opengl/kis_opengl_image_textures.h"
#ifdef HAVE_OPENGL
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLContext>
#include <QMessageBox>
#include <QApplication>
#include <QDesktopWidget>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColorModelStandardIds.h>
#include "kis_image.h"
#include "kis_config.h"
#include "KisPart.h"
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
KisOpenGLImageTextures::ImageTexturesMap KisOpenGLImageTextures::imageTexturesMap;
KisOpenGLImageTextures::KisOpenGLImageTextures()
: m_image(0)
, m_monitorProfile(0)
, m_tilesDestinationColorSpace(0)
, m_internalColorManagementActive(true)
, m_checkerTexture(0)
, m_glFuncs(0)
, m_allChannelsSelected(true)
, m_useOcio(false)
, m_initialized(false)
{
KisConfig cfg;
m_renderingIntent = (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent();
m_conversionFlags = KoColorConversionTransformation::HighQuality;
if (cfg.useBlackPointCompensation()) m_conversionFlags |= KoColorConversionTransformation::BlackpointCompensation;
if (!cfg.allowLCMSOptimization()) m_conversionFlags |= KoColorConversionTransformation::NoOptimization;
m_useOcio = cfg.useOcio();
}
KisOpenGLImageTextures::KisOpenGLImageTextures(KisImageWSP image,
const KoColorProfile *monitorProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
: m_image(image)
, m_monitorProfile(monitorProfile)
, m_renderingIntent(renderingIntent)
, m_conversionFlags(conversionFlags)
, m_tilesDestinationColorSpace(0)
, m_internalColorManagementActive(true)
, m_checkerTexture(0)
, m_glFuncs(0)
, m_allChannelsSelected(true)
, m_useOcio(false)
, m_initialized(false)
{
Q_ASSERT(renderingIntent < 4);
}
void KisOpenGLImageTextures::initGL(QOpenGLFunctions *f) {
m_glFuncs = f;
getTextureSize(&m_texturesInfo);
m_glFuncs->glGenTextures(1, &m_checkerTexture);
createImageTextureTiles();
KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds());
recalculateCache(info);
}
KisOpenGLImageTextures::~KisOpenGLImageTextures()
{
ImageTexturesMap::iterator it = imageTexturesMap.find(m_image);
if (it != imageTexturesMap.end()) {
KisOpenGLImageTextures *textures = it.value();
if (textures == this) {
dbgUI << "Removing shared image context from map";
imageTexturesMap.erase(it);
}
}
destroyImageTextureTiles();
m_glFuncs->glDeleteTextures(1, &m_checkerTexture);
}
bool KisOpenGLImageTextures::imageCanShareTextures()
{
KisConfig cfg;
if (cfg.useOcio()) return false;
if (KisPart::instance()->mainwindowCount() == 1) return true;
if (qApp->desktop()->screenCount() == 1) return true;
for (int i = 1; i < qApp->desktop()->screenCount(); i++) {
if (cfg.displayProfile(i) != cfg.displayProfile(i - 1)) {
return false;
}
}
return true;
}
KisOpenGLImageTexturesSP KisOpenGLImageTextures::getImageTextures(KisImageWSP image,
const KoColorProfile *monitorProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
{
if (imageCanShareTextures()) {
ImageTexturesMap::iterator it = imageTexturesMap.find(image);
if (it != imageTexturesMap.end()) {
KisOpenGLImageTexturesSP textures = it.value();
textures->setMonitorProfile(monitorProfile, renderingIntent, conversionFlags);
return textures;
} else {
KisOpenGLImageTextures *imageTextures = new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags);
imageTexturesMap[image] = imageTextures;
dbgUI << "Added shareable textures to map";
return imageTextures;
}
} else {
return new KisOpenGLImageTextures(image, monitorProfile, renderingIntent, conversionFlags);
}
}
QRect KisOpenGLImageTextures::calculateTileRect(int col, int row) const
{
return m_image->bounds() &
QRect(col * m_texturesInfo.effectiveWidth,
row * m_texturesInfo.effectiveHeight,
m_texturesInfo.effectiveWidth,
m_texturesInfo.effectiveHeight);
}
void KisOpenGLImageTextures::createImageTextureTiles()
{
KisConfig cfg;
destroyImageTextureTiles();
updateTextureFormat();
m_storedImageBounds = m_image->bounds();
const int lastCol = xToCol(m_image->width());
const int lastRow = yToRow(m_image->height());
if (lastCol == 0) {
return;
}
m_numCols = lastCol + 1;
// Default color is transparent black
const int pixelSize = m_tilesDestinationColorSpace->pixelSize();
QByteArray emptyTileData((m_texturesInfo.width) * (m_texturesInfo.height) * pixelSize, 0);
KisConfig config;
KisTextureTile::FilterMode mode = (KisTextureTile::FilterMode)config.openGLFilteringMode();
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx) {
QOpenGLFunctions *f = ctx->functions();
m_initialized = true;
- qDebug() << "OpenGL: creating texture tiles of size" << m_texturesInfo.height << "x" << m_texturesInfo.width;
+ dbgKrita << "OpenGL: creating texture tiles of size" << m_texturesInfo.height << "x" << m_texturesInfo.width;
for (int row = 0; row <= lastRow; row++) {
for (int col = 0; col <= lastCol; col++) {
QRect tileRect = calculateTileRect(col, row);
KisTextureTile *tile = new KisTextureTile(tileRect,
&m_texturesInfo,
emptyTileData,
mode,
cfg.useOpenGLTextureBuffer(),
cfg.numMipmapLevels(),
f);
m_textureTiles.append(tile);
}
}
}
else {
- qDebug() << "Tried to init texture tiles without a current OpenGL Context.";
+ dbgKrita << "Tried to init texture tiles without a current OpenGL Context.";
}
}
void KisOpenGLImageTextures::destroyImageTextureTiles()
{
if(m_textureTiles.isEmpty()) return;
foreach(KisTextureTile *tile, m_textureTiles) {
delete tile;
}
m_textureTiles.clear();
m_storedImageBounds = QRect();
}
KisOpenGLUpdateInfoSP KisOpenGLImageTextures::updateCache(const QRect& rect)
{
KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo();
QRect updateRect = rect & m_image->bounds();
if (updateRect.isEmpty() || !(m_initialized)) return info;
/**
* Why the rect is artificial? That's easy!
* It does not represent any real piece of the image. It is
* intentionally stretched to get through the overlappping
* stripes of neutrality and poke neighbouring tiles.
* Thanks to the rect we get the coordinates of all the tiles
* involved into update process
*/
QRect artificialRect = stretchRect(updateRect, m_texturesInfo.border);
artificialRect &= m_image->bounds();
int firstColumn = xToCol(artificialRect.left());
int lastColumn = xToCol(artificialRect.right());
int firstRow = yToRow(artificialRect.top());
int lastRow = yToRow(artificialRect.bottom());
QBitArray channelFlags; // empty by default
if (m_channelFlags.size() != m_image->projection()->colorSpace()->channels().size()) {
setChannelFlags(QBitArray());
}
if (!m_useOcio) { // Ocio does its own channel flipping
if (!m_allChannelsSelected) { // and we do it only if necessary
channelFlags = m_channelFlags;
}
}
qint32 numItems = (lastColumn - firstColumn + 1) * (lastRow - firstRow + 1);
info->tileList.reserve(numItems);
const KoColorSpace *dstCS = m_tilesDestinationColorSpace;
for (int col = firstColumn; col <= lastColumn; col++) {
for (int row = firstRow; row <= lastRow; row++) {
QRect tileRect = calculateTileRect(col, row);
QRect tileTextureRect = stretchRect(tileRect, m_texturesInfo.border);
KisTextureTileUpdateInfoSP tileInfo(
new KisTextureTileUpdateInfo(col, row,
tileTextureRect,
updateRect,
m_image->bounds()));
// Don't update empty tiles
if (tileInfo->valid()) {
tileInfo->retrieveData(m_image, channelFlags, m_onlyOneChannelSelected, m_selectedChannelIndex);
tileInfo->convertTo(dstCS, m_renderingIntent, m_conversionFlags);
info->tileList.append(tileInfo);
}
else {
- kWarning() << "Trying to create an empty tileinfo record" << col << row << tileTextureRect << updateRect << m_image->bounds();
+ dbgKrita << "Trying to create an empty tileinfo record" << col << row << tileTextureRect << updateRect << m_image->bounds();
}
}
}
info->assignDirtyImageRect(rect);
return info;
}
void KisOpenGLImageTextures::recalculateCache(KisUpdateInfoSP info)
{
if (!m_initialized) {
- qDebug() << "OpenGL: Tried to edit image texture cache before it was initialized.";
+ dbgKrita << "OpenGL: Tried to edit image texture cache before it was initialized.";
return;
}
KisOpenGLUpdateInfoSP glInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
if(!glInfo) return;
KisTextureTileUpdateInfoSP tileInfo;
foreach(tileInfo, glInfo->tileList) {
KisTextureTile *tile = getTextureTileCR(tileInfo->tileCol(), tileInfo->tileRow());
KIS_ASSERT_RECOVER_RETURN(tile);
tile->update(*tileInfo);
}
}
void KisOpenGLImageTextures::generateCheckerTexture(const QImage &checkImage)
{
if (m_glFuncs) {
- qDebug() << "Attaching checker texture" << checkerTexture();
+ dbgKrita << "Attaching checker texture" << checkerTexture();
m_glFuncs->glBindTexture(GL_TEXTURE_2D, checkerTexture());
m_glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
m_glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
m_glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
m_glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
m_glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
QImage img = checkImage;
if (checkImage.width() != BACKGROUND_TEXTURE_SIZE || checkImage.height() != BACKGROUND_TEXTURE_SIZE) {
img = checkImage.scaled(BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE);
}
m_glFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BACKGROUND_TEXTURE_SIZE, BACKGROUND_TEXTURE_SIZE,
0, GL_BGRA, GL_UNSIGNED_BYTE, img.constBits());
}
else {
- qDebug() << "OpenGL: Tried to generate checker texture before OpenGL was initialized.";
+ dbgKrita << "OpenGL: Tried to generate checker texture before OpenGL was initialized.";
}
}
GLuint KisOpenGLImageTextures::checkerTexture()
{
if (m_glFuncs) {
if (m_checkerTexture == 0) {
m_glFuncs->glGenTextures(1, &m_checkerTexture);
}
return m_checkerTexture;
}
else {
return 0;
}
}
void KisOpenGLImageTextures::updateConfig(bool useBuffer, int NumMipmapLevels)
{
if(m_textureTiles.isEmpty()) return;
foreach(KisTextureTile *tile, m_textureTiles) {
tile->setUseBuffer(useBuffer);
tile->setNumMipmapLevels(NumMipmapLevels);
}
}
void KisOpenGLImageTextures::slotImageSizeChanged(qint32 /*w*/, qint32 /*h*/)
{
createImageTextureTiles();
}
void KisOpenGLImageTextures::setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
- //qDebug() << "Setting monitor profile to" << monitorProfile->name() << renderingIntent << conversionFlags;
+ //dbgKrita << "Setting monitor profile to" << monitorProfile->name() << renderingIntent << conversionFlags;
m_monitorProfile = monitorProfile;
m_renderingIntent = renderingIntent;
m_conversionFlags = conversionFlags;
createImageTextureTiles();
}
void KisOpenGLImageTextures::setChannelFlags(const QBitArray &channelFlags)
{
m_channelFlags = channelFlags;
int selectedChannels = 0;
const KoColorSpace *projectionCs = m_image->projection()->colorSpace();
QList<KoChannelInfo*> channelInfo = projectionCs->channels();
if (m_channelFlags.size() != channelInfo.size()) {
m_channelFlags = QBitArray();
}
for (int i = 0; i < m_channelFlags.size(); ++i) {
if (m_channelFlags.testBit(i) && channelInfo[i]->channelType() == KoChannelInfo::COLOR) {
selectedChannels++;
m_selectedChannelIndex = i;
}
}
m_allChannelsSelected = (selectedChannels == m_channelFlags.size());
m_onlyOneChannelSelected = (selectedChannels == 1);
}
void KisOpenGLImageTextures::getTextureSize(KisGLTexturesInfo *texturesInfo)
{
KisConfig cfg;
const GLint preferredTextureSize = cfg.openGLTextureSize();
GLint maxTextureSize;
if (m_glFuncs) {
m_glFuncs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
else {
- qDebug() << "OpenGL: Tried to read texture size before OpenGL was initialized.";
+ dbgKrita << "OpenGL: Tried to read texture size before OpenGL was initialized.";
maxTextureSize = GL_MAX_TEXTURE_SIZE;
}
texturesInfo->width = qMin(preferredTextureSize, maxTextureSize);
texturesInfo->height = qMin(preferredTextureSize, maxTextureSize);
texturesInfo->border = cfg.textureOverlapBorder();
texturesInfo->effectiveWidth = texturesInfo->width - 2 * texturesInfo->border;
texturesInfo->effectiveHeight = texturesInfo->height - 2 * texturesInfo->border;
}
bool KisOpenGLImageTextures::internalColorManagementActive() const
{
return m_internalColorManagementActive;
}
bool KisOpenGLImageTextures::setInternalColorManagementActive(bool value)
{
bool needsFinalRegeneration = m_internalColorManagementActive != value;
if (needsFinalRegeneration) {
m_internalColorManagementActive = value;
createImageTextureTiles();
// at this point the value of m_internalColorManagementActive might
// have been forcely reverted to 'false' in case of some problems
}
return needsFinalRegeneration;
}
void KisOpenGLImageTextures::updateTextureFormat()
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (!(m_image && ctx)) return;
m_texturesInfo.internalFormat = GL_RGBA8;
m_texturesInfo.type = GL_UNSIGNED_BYTE;
m_texturesInfo.format = GL_BGRA;
KoID colorModelId = m_image->colorSpace()->colorModelId();
KoID colorDepthId = m_image->colorSpace()->colorDepthId();
KoID destinationColorModelId = RGBAColorModelID;
KoID destinationColorDepthId = Integer8BitsColorDepthID;
dbgUI << "Choosing texture format:";
if (colorModelId == RGBAColorModelID) {
if (colorDepthId == Float16BitsColorDepthID) {
if (ctx->hasExtension("GL_ARB_texture_float")) {
m_texturesInfo.internalFormat = GL_RGBA16F_ARB;
dbgUI << "Using ARB half";
}
else if (ctx->hasExtension("GL_ATI_texture_float")) {
m_texturesInfo.internalFormat = GL_RGBA_FLOAT16_ATI;
dbgUI << "Using ATI half";
}
bool haveBuiltInOpenExr = false;
#ifdef HAVE_OPENEXR
haveBuiltInOpenExr = true;
#endif
if (haveBuiltInOpenExr && ctx->hasExtension("GL_ARB_half_float_pixel")) {
m_texturesInfo.type = GL_HALF_FLOAT_ARB;
destinationColorDepthId = Float16BitsColorDepthID;
dbgUI << "Pixel type half";
} else {
m_texturesInfo.type = GL_FLOAT;
destinationColorDepthId = Float32BitsColorDepthID;
dbgUI << "Pixel type float";
}
m_texturesInfo.format = GL_RGBA;
}
else if (colorDepthId == Float32BitsColorDepthID) {
if (ctx->hasExtension("GL_ARB_texture_float")) {
m_texturesInfo.internalFormat = GL_RGBA32F_ARB;
dbgUI << "Using ARB float";
} else if (ctx->hasExtension("GL_ATI_texture_float")) {
m_texturesInfo.internalFormat = GL_RGBA_FLOAT32_ATI;
dbgUI << "Using ATI float";
}
m_texturesInfo.type = GL_FLOAT;
m_texturesInfo.format = GL_RGBA;
destinationColorDepthId = Float32BitsColorDepthID;
}
else if (colorDepthId == Integer16BitsColorDepthID) {
m_texturesInfo.internalFormat = GL_RGBA16;
m_texturesInfo.type = GL_UNSIGNED_SHORT;
m_texturesInfo.format = GL_BGRA;
destinationColorDepthId = Integer16BitsColorDepthID;
dbgUI << "Using 16 bits rgba";
}
}
else {
// We will convert the colorspace to 16 bits rgba, instead of 8 bits
if (colorDepthId == Integer16BitsColorDepthID) {
m_texturesInfo.internalFormat = GL_RGBA16;
m_texturesInfo.type = GL_UNSIGNED_SHORT;
m_texturesInfo.format = GL_BGRA;
destinationColorDepthId = Integer16BitsColorDepthID;
dbgUI << "Using conversion to 16 bits rgba";
}
}
if (!m_internalColorManagementActive &&
colorModelId != destinationColorModelId) {
KisConfig cfg;
KisConfig::OcioColorManagementMode cm = cfg.ocioColorManagementMode();
if (cm != KisConfig::INTERNAL) {
QMessageBox::critical(0,
i18nc("@title:window", "Krita"),
i18n("You enabled OpenColorIO based color management, but your image is not an RGB image.\n"
"OpenColorIO-based color management only works with RGB images.\n"
"Please check the settings in the LUT docker."
"OpenColorIO will now be deactivated."));
}
- qWarning() << "WARNING: Internal color management was forcely enabled";
- qWarning() << "Color Management Mode: " << cm;
- qWarning() << ppVar(m_image->colorSpace());
- qWarning() << ppVar(destinationColorModelId);
- qWarning() << ppVar(destinationColorDepthId);
+ warnKrita << "WARNING: Internal color management was forcely enabled";
+ warnKrita << "Color Management Mode: " << cm;
+ warnKrita << ppVar(m_image->colorSpace());
+ warnKrita << ppVar(destinationColorModelId);
+ warnKrita << ppVar(destinationColorDepthId);
cfg.setOcioColorManagementMode(KisConfig::INTERNAL);
m_internalColorManagementActive = true;
}
const KoColorProfile *profile =
m_internalColorManagementActive ||
colorModelId != destinationColorModelId ?
m_monitorProfile : m_image->colorSpace()->profile();
/**
* TODO: add an optimization so that the tile->convertTo() method
* would not be called when not needed (DK)
*/
m_tilesDestinationColorSpace =
KoColorSpaceRegistry::instance()->colorSpace(destinationColorModelId.id(),
destinationColorDepthId.id(),
profile);
}
#endif // HAVE_OPENGL
diff --git a/krita/ui/opengl/kis_texture_tile.cpp b/krita/ui/opengl/kis_texture_tile.cpp
index 000826e4bf1..aa80623faea 100644
--- a/krita/ui/opengl/kis_texture_tile.cpp
+++ b/krita/ui/opengl/kis_texture_tile.cpp
@@ -1,330 +1,330 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define GL_GLEXT_PROTOTYPES
#include "kis_texture_tile.h"
#ifdef HAVE_OPENGL
-#include <QDebug>
+#include <kis_debug.h>
#include <QOpenGLFunctions>
#ifndef GL_BGRA
#define GL_BGRA 0x814F
#endif
inline QRectF relativeRect(const QRect &br /* baseRect */,
const QRect &cr /* childRect */,
const KisGLTexturesInfo *texturesInfo)
{
const qreal x = qreal(cr.x() - br.x()) / texturesInfo->width;
const qreal y = qreal(cr.y() - br.y()) / texturesInfo->height;
const qreal w = qreal(cr.width()) / texturesInfo->width;
const qreal h = qreal(cr.height()) / texturesInfo->height;
return QRectF(x, y, w, h);
}
KisTextureTile::KisTextureTile(QRect imageRect, const KisGLTexturesInfo *texturesInfo,
const QByteArray &fillData, FilterMode filter,
bool useBuffer, int numMipmapLevels, QOpenGLFunctions *fcn)
: m_textureId(0)
#ifdef USE_PIXEL_BUFFERS
, m_glBuffer(0)
#endif
, m_tileRectInImagePixels(imageRect)
, m_filter(filter)
, m_texturesInfo(texturesInfo)
, m_needsMipmapRegeneration(false)
, m_useBuffer(useBuffer)
, m_numMipmapLevels(numMipmapLevels)
, f(fcn)
{
const GLvoid *fd = fillData.constData();
m_textureRectInImagePixels =
stretchRect(m_tileRectInImagePixels, texturesInfo->border);
m_tileRectInTexturePixels = relativeRect(m_textureRectInImagePixels,
m_tileRectInImagePixels,
m_texturesInfo);
f->glGenTextures(1, &m_textureId);
f->glBindTexture(GL_TEXTURE_2D, m_textureId);
setTextureParameters();
#ifdef USE_PIXEL_BUFFERS
createTextureBuffer(fillData.constData(), fillData.size());
// we set fill data to 0 so the next glTexImage2D call uses our buffer
fd = 0;
#endif
f->glTexImage2D(GL_TEXTURE_2D, 0,
m_texturesInfo->internalFormat,
m_texturesInfo->width,
m_texturesInfo->height, 0,
m_texturesInfo->format,
m_texturesInfo->type, fd);
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
m_glBuffer->release();
}
#endif
setNeedsMipmapRegeneration();
}
KisTextureTile::~KisTextureTile()
{
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
delete m_glBuffer;
}
#endif
f->glDeleteTextures(1, &m_textureId);
}
void KisTextureTile::bindToActiveTexture()
{
f->glBindTexture(GL_TEXTURE_2D, m_textureId);
if (m_needsMipmapRegeneration) {
f->glGenerateMipmap(GL_TEXTURE_2D);
m_needsMipmapRegeneration = false;
}
}
void KisTextureTile::setNeedsMipmapRegeneration()
{
if (m_filter == TrilinearFilterMode ||
m_filter == HighQualityFiltering) {
m_needsMipmapRegeneration = true;
}
}
void KisTextureTile::update(const KisTextureTileUpdateInfo &updateInfo)
{
f->initializeOpenGLFunctions();
f->glBindTexture(GL_TEXTURE_2D, m_textureId);
setTextureParameters();
const GLvoid *fd = updateInfo.data();
#ifdef USE_PIXEL_BUFFERS
if (!m_glBuffer) {
createTextureBuffer((const char*)updateInfo.data(), updateInfo.patchPixelsLength());
}
#endif
if (updateInfo.isEntireTileUpdated()) {
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
m_glBuffer->bind();
m_glBuffer->allocate(updateInfo.patchPixelsLength());
void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly);
memcpy(vid, fd, updateInfo.patchPixelsLength());
m_glBuffer->unmap();
// we set fill data to 0 so the next glTexImage2D call uses our buffer
fd = 0;
}
#endif
f->glTexImage2D(GL_TEXTURE_2D, 0,
m_texturesInfo->internalFormat,
m_texturesInfo->width,
m_texturesInfo->height, 0,
m_texturesInfo->format,
m_texturesInfo->type,
fd);
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
m_glBuffer->release();
}
#endif
}
else {
QPoint patchOffset = updateInfo.patchOffset();
QSize patchSize = updateInfo.patchSize();
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
m_glBuffer->bind();
quint32 size = patchSize.width() * patchSize.height() * updateInfo.pixelSize();
m_glBuffer->allocate(size);
void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly);
memcpy(vid, fd, size);
m_glBuffer->unmap();
// we set fill data to 0 so the next glTexImage2D call uses our buffer
fd = 0;
}
#endif
f->glTexSubImage2D(GL_TEXTURE_2D, 0,
patchOffset.x(), patchOffset.y(),
patchSize.width(), patchSize.height(),
m_texturesInfo->format,
m_texturesInfo->type,
fd);
#ifdef USE_PIXEL_BUFFERS
if (m_useBuffer) {
m_glBuffer->release();
}
#endif
}
/**
* On the boundaries of KisImage, there is a border-effect as well.
* So we just repeat the bounding pixels of the image to make
* bilinear interpolator happy.
*/
/**
* WARN: The width of the stripes will be equal to the broder
* width of the tiles.
*/
const int pixelSize = updateInfo.pixelSize();
const QRect imageRect = updateInfo.imageRect();
const QPoint patchOffset = updateInfo.patchOffset();
const QSize patchSize = updateInfo.patchSize();
const QRect patchRect = QRect(m_textureRectInImagePixels.topLeft() +
patchOffset,
patchSize);
const QSize tileSize = updateInfo.tileRect().size();
if(imageRect.top() == patchRect.top()) {
int start = 0;
int end = patchOffset.y() - 1;
for (int i = start; i <= end; i++) {
f->glTexSubImage2D(GL_TEXTURE_2D, 0,
patchOffset.x(), i,
patchSize.width(), 1,
m_texturesInfo->format,
m_texturesInfo->type,
updateInfo.data());
}
}
if (imageRect.bottom() == patchRect.bottom()) {
int shift = patchSize.width() * (patchSize.height() - 1) *
pixelSize;
int start = patchOffset.y() + patchSize.height();
int end = tileSize.height() - 1;
for (int i = start; i < end; i++) {
f->glTexSubImage2D(GL_TEXTURE_2D, 0,
patchOffset.x(), i,
patchSize.width(), 1,
m_texturesInfo->format,
m_texturesInfo->type,
updateInfo.data() + shift);
}
}
if (imageRect.left() == patchRect.left()) {
QByteArray columnBuffer(patchSize.height() * pixelSize, 0);
quint8 *srcPtr = updateInfo.data();
quint8 *dstPtr = (quint8*) columnBuffer.data();
for(int i = 0; i < patchSize.height(); i++) {
memcpy(dstPtr, srcPtr, pixelSize);
srcPtr += patchSize.width() * pixelSize;
dstPtr += pixelSize;
}
int start = 0;
int end = patchOffset.x() - 1;
for (int i = start; i <= end; i++) {
f->glTexSubImage2D(GL_TEXTURE_2D, 0,
i, patchOffset.y(),
1, patchSize.height(),
m_texturesInfo->format,
m_texturesInfo->type,
columnBuffer.constData());
}
}
if (imageRect.right() == patchRect.right()) {
QByteArray columnBuffer(patchSize.height() * pixelSize, 0);
quint8 *srcPtr = updateInfo.data() + (patchSize.width() - 1) * pixelSize;
quint8 *dstPtr = (quint8*) columnBuffer.data();
for(int i = 0; i < patchSize.height(); i++) {
memcpy(dstPtr, srcPtr, pixelSize);
srcPtr += patchSize.width() * pixelSize;
dstPtr += pixelSize;
}
int start = patchOffset.x() + patchSize.width();
int end = tileSize.width() - 1;
for (int i = start; i <= end; i++) {
f->glTexSubImage2D(GL_TEXTURE_2D, 0,
i, patchOffset.y(),
1, patchSize.height(),
m_texturesInfo->format,
m_texturesInfo->type,
columnBuffer.constData());
}
}
setNeedsMipmapRegeneration();
}
#ifdef USE_PIXEL_BUFFERS
void KisTextureTile::createTextureBuffer(const char *data, int size)
{
if (m_useBuffer) {
if (!m_glBuffer) {
m_glBuffer = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
m_glBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
m_glBuffer->create();
m_glBuffer->bind();
m_glBuffer->allocate(size);
}
void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly);
memcpy(vid, data, size);
m_glBuffer->unmap();
}
else {
m_glBuffer = 0;
}
}
#endif
#endif /* HAVE_OPENGL */
diff --git a/krita/ui/qtsingleapplication/qtlocalpeer.cpp b/krita/ui/qtsingleapplication/qtlocalpeer.cpp
index e1080dc8de0..1a6fe562377 100644
--- a/krita/ui/qtsingleapplication/qtlocalpeer.cpp
+++ b/krita/ui/qtsingleapplication/qtlocalpeer.cpp
@@ -1,180 +1,180 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qtlocalpeer.h"
#include <QCoreApplication>
#include <QDataStream>
#include <QTime>
#if defined(Q_OS_WIN)
#include <QLibrary>
#include <qt_windows.h>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif
#if defined(Q_OS_UNIX)
#include <time.h>
#include <unistd.h>
#endif
static const char ack[] = "ack";
QString QtLocalPeer::appSessionId(const QString &appId)
{
QByteArray idc = appId.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
//### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best.
QString res = QLatin1String("qtsingleapplication-")
+ QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
QLibrary lib(QLatin1String("kernel32"));
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
res += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
res += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
return res;
}
QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
: QObject(parent), id(appId)
{
if (id.isEmpty())
id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
socketName = appSessionId(id);
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLockedFile::WriteLock, false))
return true;
if (!QLocalServer::removeServer(socketName))
qWarning("QtSingleCoreApplication: could not cleanup socket");
bool res = server->listen(socketName);
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
}
bool QtLocalPeer::sendMessage(const QByteArray &message, int timeout, bool block)
{
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for (int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
#if defined(Q_OS_WIN)
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
if (!connOk)
return false;
QByteArray uMsg(message);
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
res &= socket.waitForReadyRead(timeout); // wait for ack
res &= (socket.read(qstrlen(ack)) == ack);
if (block) // block until peer disconnects
socket.waitForDisconnected(-1);
return res;
}
void QtLocalPeer::receiveConnection()
{
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
// Why doesn't Qt have a blocking stream that takes care of this shait???
while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32))) {
if (!socket->isValid()) // stale request
return;
socket->waitForReadyRead(1000);
}
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
- //qDebug() << "RCV: remaining" << remaining;
+ //dbgKrita << "RCV: remaining" << remaining;
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
//qDebug() << "RCV: got" << got << "remaining" << remaining;
} while (remaining && got >= 0 && socket->waitForReadyRead(2000));
//### error check: got<0
if (got < 0) {
qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
delete socket;
return;
}
// ### async this
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
emit messageReceived(uMsg, socket); // ##(might take a long time to return)
}
diff --git a/krita/ui/qtsingleapplication/qtsingleapplication.cpp b/krita/ui/qtsingleapplication/qtsingleapplication.cpp
index e15abe16b92..6345b807fb2 100644
--- a/krita/ui/qtsingleapplication/qtsingleapplication.cpp
+++ b/krita/ui/qtsingleapplication/qtsingleapplication.cpp
@@ -1,192 +1,192 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <qtlockedfile.h>
#include <QDir>
#include <QFileOpenEvent>
#include <QSharedMemory>
#include <QWidget>
static const int instancesSize = 1024;
static QString instancesLockFilename(const QString &appSessionId)
{
const QChar slash(QLatin1Char('/'));
QString res = QDir::tempPath();
if (!res.endsWith(slash))
res += slash;
return res + appSessionId + QLatin1String("-instances");
}
-QtSingleApplication::QtSingleApplication(const QString &appId, int &/*argc*/, char **/*argv*/)
- : KApplication(),
+QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
+ : QApplication(argc, argv),
firstPeer(-1),
pidPeer(0)
{
this->appId = appId;
const QString appSessionId = QtLocalPeer::appSessionId(appId);
// This shared memory holds a zero-terminated array of active (or crashed) instances
instances = new QSharedMemory(appSessionId, this);
actWin = 0;
block = false;
// First instance creates the shared memory, later instances attach to it
const bool created = instances->create(instancesSize);
if (!created) {
if (!instances->attach()) {
qWarning() << "Failed to initialize instances shared memory: "
<< instances->errorString();
delete instances;
instances = 0;
return;
}
}
// QtLockedFile is used to workaround QTBUG-10364
QtLockedFile lockfile(instancesLockFilename(appSessionId));
lockfile.open(QtLockedFile::ReadWrite);
lockfile.lock(QtLockedFile::WriteLock);
qint64 *pids = static_cast<qint64 *>(instances->data());
if (!created) {
// Find the first instance that it still running
// The whole list needs to be iterated in order to append to it
for (; *pids; ++pids) {
if (firstPeer == -1 && isRunning(*pids))
firstPeer = *pids;
}
}
// Add current pid to list and terminate it
*pids++ = QCoreApplication::applicationPid();
*pids = 0;
pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') +
QString::number(QCoreApplication::applicationPid()));
connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), SIGNAL(messageReceived(QByteArray,QObject*)));
pidPeer->isClient();
lockfile.unlock();
}
QtSingleApplication::~QtSingleApplication()
{
if (!instances)
return;
const qint64 appPid = QCoreApplication::applicationPid();
QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
lockfile.open(QtLockedFile::ReadWrite);
lockfile.lock(QtLockedFile::WriteLock);
// Rewrite array, removing current pid and previously crashed ones
qint64 *pids = static_cast<qint64 *>(instances->data());
qint64 *newpids = pids;
for (; *pids; ++pids) {
if (*pids != appPid && isRunning(*pids))
*newpids++ = *pids;
}
*newpids = 0;
lockfile.unlock();
}
bool QtSingleApplication::event(QEvent *event)
{
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *foe = static_cast<QFileOpenEvent*>(event);
emit fileOpenRequest(foe->file());
return true;
}
return QApplication::event(event);
}
bool QtSingleApplication::isRunning(qint64 pid)
{
if (pid == -1) {
pid = firstPeer;
if (pid == -1) {
return false;
}
}
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.isClient();
}
bool QtSingleApplication::sendMessage(const QByteArray &message, int timeout, qint64 pid)
{
if (pid == -1) {
pid = firstPeer;
if (pid == -1)
return false;
}
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.sendMessage(message, timeout, block);
}
QString QtSingleApplication::applicationId() const
{
return appId;
}
void QtSingleApplication::setBlock(bool value)
{
block = value;
}
void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage)
{
actWin = aw;
if (!pidPeer)
return;
if (activateOnMessage)
connect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow()));
else
disconnect(pidPeer, SIGNAL(messageReceived(QByteArray,QObject*)), this, SLOT(activateWindow()));
}
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
void QtSingleApplication::activateWindow()
{
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
}
diff --git a/krita/ui/qtsingleapplication/qtsingleapplication.h b/krita/ui/qtsingleapplication/qtsingleapplication.h
index d70cf0f2054..efa75bf7609 100644
--- a/krita/ui/qtsingleapplication/qtsingleapplication.h
+++ b/krita/ui/qtsingleapplication/qtsingleapplication.h
@@ -1,80 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QTSINGLEAPPLICATION
#define QTSINGLEAPPLICATION
#include <QApplication>
-#include <kapplication.h>
QT_FORWARD_DECLARE_CLASS(QSharedMemory)
class QtLocalPeer;
#include <kritaui_export.h>
-class KRITAUI_EXPORT QtSingleApplication : public KApplication
+class KRITAUI_EXPORT QtSingleApplication : public QApplication
{
Q_OBJECT
public:
QtSingleApplication(const QString &id, int &argc, char **argv);
~QtSingleApplication();
bool isRunning(qint64 pid = -1);
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
bool event(QEvent *event);
QString applicationId() const;
void setBlock(bool value);
public Q_SLOTS:
bool sendMessage(const QByteArray &message, int timeout = 5000, qint64 pid = -1);
void activateWindow();
Q_SIGNALS:
void messageReceived(const QByteArray &message, QObject *socket);
void fileOpenRequest(const QString &file);
private:
QString instancesFileName(const QString &appId);
qint64 firstPeer;
QSharedMemory *instances;
QtLocalPeer *pidPeer;
QWidget *actWin;
QString appId;
bool block;
};
#endif
diff --git a/krita/ui/tests/kis_action_manager_test.cpp b/krita/ui/tests/kis_action_manager_test.cpp
index 524a47fea15..faa641b6816 100644
--- a/krita/ui/tests/kis_action_manager_test.cpp
+++ b/krita/ui/tests/kis_action_manager_test.cpp
@@ -1,136 +1,136 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_action_manager_test.h"
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KisPart.h>
#include <KisMainWindow.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <KisView.h>
#include <util.h>
#include <kis_action.h>
#include <kis_action_manager.h>
#include <KisViewManager.h>
#include "kis_node_manager.h"
void KisActionManagerTest::testUpdateGUI()
{
KisDocument* doc = createEmptyDocument();
KisMainWindow* mainWindow = KisPart::instance()->createMainWindow();
QPointer<KisView> view = new KisView(doc, mainWindow->resourceManager(), mainWindow->actionCollection(), mainWindow);
KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection());
KisPart::instance()->addView(view);
mainWindow->showView(view);
view->setViewManager(viewManager);
viewManager->setCurrentView(view);
KisAction* action = new KisAction("dummy", this);
action->setActivationFlags(KisAction::ACTIVE_DEVICE);
view->viewManager()->actionManager()->addAction("dummy", action);
KisAction* action2 = new KisAction("dummy", this);
action2->setActivationFlags(KisAction::ACTIVE_SHAPE_LAYER);
view->viewManager()->actionManager()->addAction("dummy", action2);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(!action->isEnabled());
QVERIFY(!action2->isEnabled());
KisPaintLayerSP paintLayer1 = new KisPaintLayer(doc->image(), "paintlayer1", OPACITY_OPAQUE_U8);
doc->image()->addNode(paintLayer1);
viewManager->nodeManager()->slotUiActivatedNode(paintLayer1);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(action->isEnabled());
QVERIFY(!action2->isEnabled());
}
void KisActionManagerTest::testCondition()
{
KisDocument* doc = createEmptyDocument();
KisMainWindow* mainWindow = KisPart::instance()->createMainWindow();
QPointer<KisView> view = new KisView(doc, mainWindow->resourceManager(), mainWindow->actionCollection(), mainWindow);
KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection());
KisPart::instance()->addView(view);
mainWindow->showView(view);
view->setViewManager(viewManager);
viewManager->setCurrentView(view);
KisAction* action = new KisAction("dummy", this);
action->setActivationFlags(KisAction::ACTIVE_DEVICE);
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
view->viewManager()->actionManager()->addAction("dummy", action);
KisPaintLayerSP paintLayer1 = new KisPaintLayer(doc->image(), "paintlayer1", OPACITY_OPAQUE_U8);
doc->image()->addNode(paintLayer1);
viewManager->nodeManager()->slotUiActivatedNode(paintLayer1);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(action->isEnabled());
// visible
// paintLayer1->setVisible(false);
// view->viewManager()->actionManager()->updateGUI();
// QVERIFY(!action->isEnabled());
paintLayer1->setVisible(true);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(action->isEnabled());
// locked
paintLayer1->setUserLocked(true);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(!action->isEnabled());
paintLayer1->setUserLocked(false);
view->viewManager()->actionManager()->updateGUI();
QVERIFY(action->isEnabled());
}
void KisActionManagerTest::testTakeAction()
{
KisDocument* doc = createEmptyDocument();
KisMainWindow* mainWindow = KisPart::instance()->createMainWindow();
QPointer<KisView> view = new KisView(doc, mainWindow->resourceManager(), mainWindow->actionCollection(), mainWindow);
KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection());
KisPart::instance()->addView(view);
mainWindow->showView(view);
view->setViewManager(viewManager);
viewManager->setCurrentView(view);
KisAction* action = new KisAction("dummy", this);
view->viewManager()->actionManager()->addAction("dummy", action);
QVERIFY(view->viewManager()->actionManager()->actionByName("dummy") != 0);
view->viewManager()->actionManager()->takeAction(action);
QVERIFY(view->viewManager()->actionManager()->actionByName("dummy") == 0);
}
QTEST_KDEMAIN(KisActionManagerTest, GUI)
diff --git a/krita/ui/tests/kis_asl_layer_style_serializer_test.cpp b/krita/ui/tests/kis_asl_layer_style_serializer_test.cpp
index 95e5c58658f..0e8c932f06a 100644
--- a/krita/ui/tests/kis_asl_layer_style_serializer_test.cpp
+++ b/krita/ui/tests/kis_asl_layer_style_serializer_test.cpp
@@ -1,422 +1,422 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_layer_style_serializer_test.h"
#include <qtest_kde.h>
#include <QDomDocument>
#include <KoCompositeOpRegistry.h>
#include <KoAbstractGradient.h>
#include <KoStopGradient.h>
#include <KoPattern.h>
#include "testutil.h"
#include "kis_psd_layer_style.h"
#include "kis_asl_layer_style_serializer.h"
#include <asl/kis_asl_reader.h>
#define CMP(object, method, value) QCOMPARE(style->object()->method(), value)
void KisAslLayerStyleSerializerTest::testReading()
{
KisAslLayerStyleSerializer s;
// QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_style.asl"));
QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
QFile aslFile(srcFileName);
aslFile.open(QIODevice::ReadOnly);
s.readFromDevice(&aslFile);
QVector<KisPSDLayerStyleSP> styles = s.styles();
QVERIFY(styles.size() == 1);
KisPSDLayerStyleSP style = styles.first();
CMP(dropShadow, effectEnabled, true);
CMP(dropShadow, blendMode, COMPOSITE_MULT);
CMP(dropShadow, color, QColor(Qt::black));
CMP(dropShadow, opacity, 15);
CMP(dropShadow, angle, -120);
CMP(dropShadow, useGlobalLight, false);
CMP(dropShadow, distance, 2);
CMP(dropShadow, spread, 1);
CMP(dropShadow, size, 7);
CMP(dropShadow, antiAliased, true);
CMP(dropShadow, noise, 3);
// CMP(dropShadow, contourLookupTable,);
CMP(innerShadow, effectEnabled, true);
CMP(innerShadow, blendMode, COMPOSITE_DARKEN);
CMP(innerShadow, color, QColor(Qt::black));
CMP(innerShadow, opacity, 28);
CMP(innerShadow, angle, 120);
CMP(innerShadow, useGlobalLight, true);
CMP(innerShadow, distance, 8);
CMP(innerShadow, spread, 15);
CMP(innerShadow, size, 27);
CMP(innerShadow, antiAliased, false);
CMP(innerShadow, noise, 10);
// CMP(innerShadow, contourLookupTable,);
CMP(outerGlow, effectEnabled, true);
CMP(outerGlow, blendMode, COMPOSITE_SCREEN);
CMP(outerGlow, color, QColor(255, 255, 189));
CMP(outerGlow, opacity, 43);
CMP(outerGlow, spread, 23);
CMP(outerGlow, size, 109);
CMP(outerGlow, antiAliased, true);
CMP(outerGlow, noise, 29);
// CMP(outerGlow, contourLookupTable,);
// CMP(outerGlow, gradient,);
CMP(outerGlow, fillType, psd_fill_solid_color);
CMP(outerGlow, technique, psd_technique_precise);
CMP(outerGlow, range, 69);
CMP(outerGlow, jitter, 18);
CMP(innerGlow, effectEnabled, true);
CMP(innerGlow, blendMode, COMPOSITE_SCREEN);
CMP(innerGlow, color, QColor(255, 255, 189));
CMP(innerGlow, opacity, 55);
CMP(innerGlow, spread, 21);
CMP(innerGlow, size, 128);
CMP(innerGlow, antiAliased, true);
CMP(innerGlow, noise, 33);
// CMP(innerGlow, contourLookupTable,);
// CMP(innerGlow, gradient,);
CMP(innerGlow, fillType, psd_fill_solid_color);
CMP(innerGlow, technique, psd_technique_softer);
CMP(innerGlow, range, 32);
CMP(innerGlow, jitter, 22);
CMP(innerGlow, source, psd_glow_edge);
CMP(satin, effectEnabled, true);
CMP(satin, blendMode, COMPOSITE_MULT);
CMP(satin, color, QColor(Qt::black));
CMP(satin, opacity, 68);
CMP(satin, angle, 19);
CMP(satin, distance, 11);
CMP(satin, size, 14);
CMP(satin, antiAliased, false);
CMP(satin, invert, true);
// CMP(satin, contourLookupTable,);
CMP(colorOverlay, effectEnabled, true);
CMP(colorOverlay, blendMode, COMPOSITE_OVER);
CMP(colorOverlay, color, QColor(Qt::red));
CMP(colorOverlay, opacity, 63);
CMP(gradientOverlay, effectEnabled, true);
CMP(gradientOverlay, blendMode, COMPOSITE_OVER);
CMP(gradientOverlay, opacity, 100);
CMP(gradientOverlay, angle, 90);
CMP(gradientOverlay, style, psd_gradient_style_linear);
CMP(gradientOverlay, reverse, false);
CMP(gradientOverlay, alignWithLayer, true);
CMP(gradientOverlay, scale, 100);
CMP(gradientOverlay, gradientXOffset, 0);
CMP(gradientOverlay, gradientYOffset, 0);
//CMP(gradientOverlay, dither, );
CMP(gradientOverlay, gradient()->name, QString("Two Color"));
CMP(stroke, effectEnabled, true);
CMP(stroke, blendMode, COMPOSITE_OVER);
CMP(stroke, opacity, 67);
CMP(stroke, size, 13);
CMP(stroke, fillType, psd_fill_solid_color);
CMP(stroke, position, psd_stroke_outside);
CMP(stroke, color, QColor(210, 33, 87));
CMP(bevelAndEmboss, effectEnabled, true);
CMP(bevelAndEmboss, highlightBlendMode, COMPOSITE_SCREEN);
CMP(bevelAndEmboss, highlightOpacity, 75);
CMP(bevelAndEmboss, highlightColor, QColor(255, 255, 255));
CMP(bevelAndEmboss, shadowBlendMode, COMPOSITE_MULT);
CMP(bevelAndEmboss, shadowOpacity, 75);
CMP(bevelAndEmboss, shadowColor, QColor(Qt::black));
CMP(bevelAndEmboss, technique, psd_technique_softer);
CMP(bevelAndEmboss, style, psd_bevel_inner_bevel);
CMP(bevelAndEmboss, useGlobalLight, true);
CMP(bevelAndEmboss, angle, 120);
CMP(bevelAndEmboss, altitude, 30);
CMP(bevelAndEmboss, depth, 83);
CMP(bevelAndEmboss, size, 49);
CMP(bevelAndEmboss, direction, psd_direction_up);
// FIXME: contour
CMP(bevelAndEmboss, glossAntiAliased, false);
CMP(bevelAndEmboss, soften, 2);
CMP(bevelAndEmboss, contourEnabled, true);
// FIXME: contour curve
CMP(bevelAndEmboss, antiAliased, true);
CMP(bevelAndEmboss, contourRange, 60);
CMP(bevelAndEmboss, textureEnabled, false);
CMP(patternOverlay, effectEnabled, true);
CMP(patternOverlay, blendMode, COMPOSITE_OVER);
CMP(patternOverlay, opacity, 100);
CMP(patternOverlay, alignWithLayer, true);
CMP(patternOverlay, scale, 100);
CMP(patternOverlay, horizontalPhase, 201);
CMP(patternOverlay, verticalPhase, 162);
CMP(patternOverlay, pattern()->name, QString("$$$/Presets/Patterns/Patterns_pat/Bubbles=Bubbles"));
CMP(patternOverlay, pattern()->filename, QString("b7334da0-122f-11d4-8bb5-e27e45023b5f.pat"));
}
void KisAslLayerStyleSerializerTest::testWriting()
{
QVector<KisPSDLayerStyleSP> styles;
QByteArray refXMLDoc;
{
KisAslLayerStyleSerializer s;
QString srcFileName(TestUtil::fetchDataFileLazy("asl/test_all_and_pattern.asl"));
QFile aslFile(srcFileName);
aslFile.open(QIODevice::ReadOnly);
s.readFromDevice(&aslFile);
styles = s.styles();
{
aslFile.seek(0);
KisAslReader reader;
QDomDocument doc = reader.readFile(&aslFile);
refXMLDoc = doc.toByteArray();
}
}
// now we have an initialized KisPSDLayerStyle object
{
KisAslLayerStyleSerializer s;
s.setStyles(styles);
QFile dstFile("test_written.asl");
dstFile.open(QIODevice::WriteOnly);
s.saveToDevice(&dstFile);
dstFile.close();
}
QByteArray resultXMLDoc;
{
QFile resultFile("test_written.asl");
resultFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&resultFile);
resultXMLDoc = doc.toByteArray();
}
QFile refXMLFile("save_round_trip_src.xml");
refXMLFile.open(QIODevice::WriteOnly);
refXMLFile.write(refXMLDoc);
refXMLFile.close();
QFile resultXMLFile("save_round_trip_dst.xml");
resultXMLFile.open(QIODevice::WriteOnly);
resultXMLFile.write(resultXMLDoc);
resultXMLFile.close();
QCOMPARE(resultXMLDoc, refXMLDoc);
}
#include <KoResourceServerProvider.h>
void KisAslLayerStyleSerializerTest::testWritingGlobalPatterns()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
KoResourceServer<KoPattern> *server = KoResourceServerProvider::instance()->patternServer();
QList<KoPattern*> sortedResources = server->sortedResources();
KoPattern *pattern = sortedResources.first();
Q_ASSERT(pattern);
- qDebug() << ppVar(pattern->name());
- qDebug() << ppVar(pattern->filename());
+ dbgKrita << ppVar(pattern->name());
+ dbgKrita << ppVar(pattern->filename());
style->patternOverlay()->setEffectEnabled(true);
style->patternOverlay()->setPattern(pattern);
{
KisAslLayerStyleSerializer s;
s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
QFile dstFile("test_written_pattern_only.asl");
dstFile.open(QIODevice::WriteOnly);
s.saveToDevice(&dstFile);
dstFile.close();
}
/*
QByteArray resultXMLDoc;
{
QFile resultFile("test_written.asl");
resultFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&resultFile);
resultXMLDoc = doc.toByteArray();
}
*/
}
void KisAslLayerStyleSerializerTest::testReadMultipleStyles()
{
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
QVector<KisPSDLayerStyleSP> styles;
{
KisAslLayerStyleSerializer s;
QString srcFileName(TestUtil::fetchDataFileLazy("asl/multiple_styles.asl"));
QFile aslFile(srcFileName);
aslFile.open(QIODevice::ReadOnly);
s.readFromDevice(&aslFile);
styles = s.styles();
}
{
KisAslLayerStyleSerializer s;
QString dstFileName("multiple_styles_out.asl");
QFile aslFile(dstFileName);
aslFile.open(QIODevice::WriteOnly);
s.setStyles(styles);
s.saveToDevice(&aslFile);
}
{
KisAslLayerStyleSerializer s;
QString srcFileName("multiple_styles_out.asl");
QFile aslFile(srcFileName);
aslFile.open(QIODevice::ReadOnly);
s.readFromDevice(&aslFile);
styles = s.styles();
- qDebug() << ppVar(styles.size());
+ dbgKrita << ppVar(styles.size());
}
}
void KisAslLayerStyleSerializerTest::testWritingGradients()
{
KoStopGradient stopGradient("");
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoGradientStop> stops;
stops << KoGradientStop(0.0, KoColor(Qt::black, cs));
stops << KoGradientStop(0.3, KoColor(Qt::red, cs));
stops << KoGradientStop(0.6, KoColor(Qt::green, cs));
stops << KoGradientStop(1.0, KoColor(Qt::white, cs));
stopGradient.setStops(stops);
}
KisPSDLayerStyleSP style(new KisPSDLayerStyle());
style->outerGlow()->setEffectEnabled(true);
style->outerGlow()->setFillType(psd_fill_gradient);
style->outerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
style->innerGlow()->setEffectEnabled(true);
style->innerGlow()->setFillType(psd_fill_gradient);
style->innerGlow()->setGradient(toQShared(new KoStopGradient(stopGradient)));
style->gradientOverlay()->setEffectEnabled(true);
style->gradientOverlay()->setGradient(toQShared(new KoStopGradient(stopGradient)));
style->stroke()->setEffectEnabled(true);
style->stroke()->setFillType(psd_fill_gradient);
style->stroke()->setGradient(toQShared(new KoStopGradient(stopGradient)));
{
KisAslLayerStyleSerializer s;
s.setStyles(QVector<KisPSDLayerStyleSP>() << style);
QFile dstFile("test_written_stop_gradient.asl");
dstFile.open(QIODevice::WriteOnly);
s.saveToDevice(&dstFile);
dstFile.close();
}
QString xmlDoc;
{
QFile resultFile("test_written_stop_gradient.asl");
resultFile.open(QIODevice::ReadOnly);
KisAslReader reader;
QDomDocument doc = reader.readFile(&resultFile);
xmlDoc = doc.toString();
}
{
// the reference document has stripped "Idnt" field which is random
QRegExp rx("<node key=\"Idnt\" type=\"Text\" value=\".+\"/>");
rx.setMinimal(true);
int pos = 0;
while ((pos = rx.indexIn(xmlDoc, pos)) != -1) {
xmlDoc.remove(pos, rx.matchedLength());
}
{
//QFile xmlFile("reference_gradients.asl.xml");
//xmlFile.open(QIODevice::WriteOnly);
//xmlFile.write(xmlDoc.toLatin1());
//xmlFile.close();
}
QString refFileName(TestUtil::fetchDataFileLazy("reference_gradients.asl.xml"));
QFile refFile(refFileName);
refFile.open(QIODevice::ReadOnly);
QString refDoc = QString(refFile.readAll());
QCOMPARE(xmlDoc, refDoc);
}
}
QTEST_KDEMAIN(KisAslLayerStyleSerializerTest, GUI)
diff --git a/krita/ui/tests/kis_coordinates_converter_test.cpp b/krita/ui/tests/kis_coordinates_converter_test.cpp
index c3decde7eb6..8e9fc7af39e 100644
--- a/krita/ui/tests/kis_coordinates_converter_test.cpp
+++ b/krita/ui/tests/kis_coordinates_converter_test.cpp
@@ -1,273 +1,273 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_coordinates_converter_test.h"
#include <QTest>
#include <qtest_kde.h>
#include <QTransform>
#include <KoZoomHandler.h>
#include <KoColorSpaceRegistry.h>
#include <kis_image.h>
#include "kis_coordinates_converter.h"
void initImage(KisImageSP *image, KoZoomHandler *zoomHandler)
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
*image = new KisImage(0, 1000, 1000, cs, "projection test");
(*image)->setResolution(100, 100);
zoomHandler->setResolution(100, 100);
}
void KisCoordinatesConverterTest::testConversion()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
converter.setImage(image);
converter.setDocumentOffset(QPoint(20,20));
converter.setCanvasWidgetSize(QSize(500,500));
converter.setZoom(1.);
QRectF testRect(100,100,100,100);
QCOMPARE(converter.imageToViewport(testRect), QRectF(80,80,100,100));
QCOMPARE(converter.viewportToImage(testRect), QRectF(120,120,100,100));
QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100));
QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100));
QCOMPARE(converter.widgetToDocument(testRect), QRectF(1.20,1.20,1,1));
QCOMPARE(converter.documentToWidget(testRect), QRectF(9980,9980,10000,10000));
QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1));
QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000));
converter.setZoom(0.5);
QCOMPARE(converter.imageToViewport(testRect), QRectF(30,30,50,50));
QCOMPARE(converter.viewportToImage(testRect), QRectF(240,240,200,200));
QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100));
QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100));
QCOMPARE(converter.widgetToDocument(testRect), QRectF(2.4,2.4,2,2));
QCOMPARE(converter.documentToWidget(testRect), QRectF(4980,4980,5000,5000));
QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1));
QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000));
}
void KisCoordinatesConverterTest::testImageCropping()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
converter.setImage(image);
converter.setDocumentOffset(QPoint(0,0));
converter.setCanvasWidgetSize(QSize(500,500));
converter.setZoom(1.);
// we do NOT crop here
QCOMPARE(converter.viewportToImage(QRectF(900,900,200,200)),
QRectF(900,900,200,200));
}
#define CHECK_TRANSFORM(trans,test,ref) QCOMPARE(trans.map(test).boundingRect(), ref)
-//#define CHECK_TRANSFORM(trans,test,ref) qDebug() << trans.map(test).boundingRect()
+//#define CHECK_TRANSFORM(trans,test,ref) dbgKrita << trans.map(test).boundingRect()
void KisCoordinatesConverterTest::testTransformations()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
converter.setImage(image);
converter.setDocumentOffset(QPoint(20,30));
converter.setCanvasWidgetSize(QSize(500,500));
QRectF testRect(100,100,100,100);
QTransform imageToWidget;
QTransform documentToWidget;
QTransform flakeToWidget;
QTransform viewportToWidget;
converter.setZoom(1.);
imageToWidget = converter.imageToWidgetTransform();
documentToWidget = converter.documentToWidgetTransform();
flakeToWidget = converter.flakeToWidgetTransform();
viewportToWidget = converter.viewportToWidgetTransform();
CHECK_TRANSFORM(imageToWidget, testRect, QRectF(80,70,100,100));
CHECK_TRANSFORM(documentToWidget, testRect, QRectF(9980,9970,10000,10000));
CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100));
CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100));
converter.setZoom(0.5);
imageToWidget = converter.imageToWidgetTransform();
documentToWidget = converter.documentToWidgetTransform();
flakeToWidget = converter.flakeToWidgetTransform();
viewportToWidget = converter.viewportToWidgetTransform();
CHECK_TRANSFORM(imageToWidget, testRect, QRectF(30,20,50,50));
CHECK_TRANSFORM(documentToWidget, testRect, QRectF(4980,4970,5000,5000));
CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100));
CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100));
}
void KisCoordinatesConverterTest::testConsistency()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
converter.setImage(image);
converter.setDocumentOffset(QPoint(20,30));
converter.setCanvasWidgetSize(QSize(500,500));
QRectF testRect(100,100,100,100);
QTransform imageToWidget;
QTransform documentToWidget;
QTransform viewportToWidget;
converter.setZoom(0.5);
imageToWidget = converter.imageToWidgetTransform();
documentToWidget = converter.documentToWidgetTransform();
viewportToWidget = converter.viewportToWidgetTransform();
QRectF fromImage = converter.viewportToWidget(converter.imageToViewport(testRect));
QRectF fromDocument = converter.documentToWidget(testRect);
QRectF fromViewport = converter.viewportToWidget(testRect);
CHECK_TRANSFORM(imageToWidget, testRect, fromImage);
CHECK_TRANSFORM(documentToWidget, testRect, fromDocument);
CHECK_TRANSFORM(viewportToWidget, testRect, fromViewport);
}
void KisCoordinatesConverterTest::testRotation()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
QSize widgetSize(1000,500);
QRectF testRect(800, 100, 300, 300);
converter.setImage(image);
converter.setDocumentOffset(QPoint(0,0));
converter.setCanvasWidgetSize(widgetSize);
converter.rotate(converter.widgetCenterPoint(), 30);
converter.setZoom(1.);
QTransform viewportToWidget = converter.viewportToWidgetTransform();
QRectF boundingRect = viewportToWidget.mapRect(testRect);
QRectF directRect = converter.viewportToWidget(testRect);
QCOMPARE(boundingRect, directRect);
QRectF referenceRect(QPointF(742.82,53.5898), QSizeF(409.808,409.808));
#define FUZZY(a,b) ((a)-(b) < 0.01)
QVERIFY(FUZZY(boundingRect.top(), referenceRect.top()));
QVERIFY(FUZZY(boundingRect.left(), referenceRect.left()));
QVERIFY(FUZZY(boundingRect.width(), referenceRect.width()));
QVERIFY(FUZZY(boundingRect.height(), referenceRect.height()));
}
void KisCoordinatesConverterTest::testMirroring()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
QSize widgetSize(500,400);
QSize flakeSize(1000,1000);
QRectF testRect(300, 100, 200, 200);
converter.setImage(image);
converter.setDocumentOffset(QPoint(200,100));
converter.setCanvasWidgetSize(widgetSize);
QTransform imageToWidget;
QTransform documentToWidget;
QTransform flakeToWidget;
QTransform viewportToWidget;
converter.mirror(converter.imageCenterInWidgetPixel(), true, false);
converter.setZoom(1.);
// image pixels == flake pixels
QRectF viewportRect = converter.imageToViewport(testRect);
QRectF widgetRect = converter.viewportToWidget(viewportRect);
QCOMPARE(widgetRect, QRectF(300,0,200,200));
QCOMPARE(viewportRect, QRectF(0,0,200,200));
QRectF roundTrip = converter.viewportToWidget(widgetRect);
QCOMPARE(roundTrip, viewportRect);
}
void KisCoordinatesConverterTest::testMirroringCanvasBiggerThanImage()
{
KisImageSP image;
KisCoordinatesConverter converter;
initImage(&image, &converter);
QSize widgetSize(2000,2000);
QSize flakeSize(1000,1000);
QRectF testRect(300, 100, 200, 200);
converter.setImage(image);
converter.setDocumentOffset(QPoint(-50,-50));
converter.setCanvasWidgetSize(widgetSize);
QTransform imageToWidget;
QTransform documentToWidget;
QTransform flakeToWidget;
QTransform viewportToWidget;
converter.mirror(converter.imageCenterInWidgetPixel(), true, false);
converter.setZoom(1.);
// image pixels == flake pixels
QRectF viewportRect = converter.imageToViewport(testRect);
QRectF widgetRect = converter.viewportToWidget(viewportRect);
QCOMPARE(widgetRect, QRectF(550,150,200,200));
QCOMPARE(viewportRect, QRectF(300,100,200,200));
}
QTEST_KDEMAIN(KisCoordinatesConverterTest, GUI)
diff --git a/krita/ui/tests/kis_input_manager_test.cpp b/krita/ui/tests/kis_input_manager_test.cpp
index 37085cbee06..c00c1a2534d 100644
--- a/krita/ui/tests/kis_input_manager_test.cpp
+++ b/krita/ui/tests/kis_input_manager_test.cpp
@@ -1,394 +1,394 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_input_manager_test.h"
#include <qtest_kde.h>
#include <QMouseEvent>
#include "input/kis_single_action_shortcut.h"
#include "input/kis_stroke_shortcut.h"
#include "input/kis_abstract_input_action.h"
#include "input/kis_shortcut_matcher.h"
void KisInputManagerTest::testSingleActionShortcut()
{
KisSingleActionShortcut s(0,0);
s.setKey(QList<Qt::Key>() << Qt::Key_Shift, Qt::Key_Space);
QVERIFY(s.match(QList<Qt::Key>() << Qt::Key_Shift, Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Control, Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>(), Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Shift, Qt::Key_Escape));
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Shift, KisSingleActionShortcut::WheelUp));
s.setWheel(QList<Qt::Key>() << Qt::Key_Shift, KisSingleActionShortcut::WheelUp);
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Shift, Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Control, Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>(), Qt::Key_Space));
QVERIFY(!s.match(QList<Qt::Key>() << Qt::Key_Shift, Qt::Key_Escape));
QVERIFY(s.match(QList<Qt::Key>() << Qt::Key_Shift, KisSingleActionShortcut::WheelUp));
}
void KisInputManagerTest::testStrokeShortcut()
{
KisStrokeShortcut s(0,0);
s.setButtons(QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
QList<Qt::MouseButton>() << Qt::LeftButton);
QVERIFY(s.matchReady(QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
QList<Qt::MouseButton>() << Qt::LeftButton));
QVERIFY(s.matchReady(QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
QList<Qt::MouseButton>()));
QVERIFY(!s.matchReady(QList<Qt::Key>() << Qt::Key_Control << Qt::Key_Alt,
QList<Qt::MouseButton>()));
QVERIFY(!s.matchReady(QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
QList<Qt::MouseButton>() << Qt::RightButton));
QVERIFY(s.matchBegin(Qt::LeftButton));
QVERIFY(!s.matchBegin(Qt::RightButton));
}
struct TestingAction : public KisAbstractInputAction
{
TestingAction() : KisAbstractInputAction("TestingAction"), m_isHighResolution(false) { reset(); }
~TestingAction() {}
void begin(int shortcut, QEvent *event) { m_beginIndex = shortcut; m_beginNonNull = event;}
void end(QEvent *event) { m_ended = true; m_endNonNull = event; }
void inputEvent(QEvent* event) { Q_UNUSED(event); m_gotInput = true; }
void reset() {
m_beginIndex = -1;
m_ended = false;
m_gotInput = false;
m_beginNonNull = false;
m_endNonNull = false;
}
bool supportsHiResInputEvents() const {
return m_isHighResolution;
}
void setHighResInputEvents(bool value) {
m_isHighResolution = value;
}
int m_beginIndex;
bool m_ended;
bool m_gotInput;
bool m_beginNonNull;
bool m_endNonNull;
bool m_isHighResolution;
};
KisSingleActionShortcut* createKeyShortcut(KisAbstractInputAction *action,
int shortcutIndex,
const QList<Qt::Key> &modifiers,
Qt::Key key)
{
KisSingleActionShortcut *s = new KisSingleActionShortcut(action, shortcutIndex);
s->setKey(modifiers, key);
return s;
}
KisStrokeShortcut* createStrokeShortcut(KisAbstractInputAction *action,
int shortcutIndex,
const QList<Qt::Key> &modifiers,
Qt::MouseButton button)
{
KisStrokeShortcut *s = new KisStrokeShortcut(action, shortcutIndex);
s->setButtons(modifiers, QList<Qt::MouseButton>() << button);
return s;
}
void KisInputManagerTest::testKeyEvents()
{
KisShortcutMatcher m;
m.enterEvent();
TestingAction *a = new TestingAction();
m.addShortcut(
createKeyShortcut(a, 10,
QList<Qt::Key>() << Qt::Key_Shift,
Qt::Key_Enter));
m.addShortcut(
createKeyShortcut(a, 11,
QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
Qt::Key_Enter));
m.addShortcut(
createStrokeShortcut(a, 12,
QList<Qt::Key>() << Qt::Key_Shift,
Qt::RightButton));
m.addShortcut(
createStrokeShortcut(a, 13,
QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
Qt::LeftButton));
QCOMPARE(a->m_beginIndex, -1);
// Test event with random values
QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(),
Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
// Press Ctrl+Shift
QVERIFY(!m.keyPressed(Qt::Key_Shift));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.keyPressed(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
// Complete Ctrl+Shift+Enter shortcut
QVERIFY(m.keyPressed(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, 11);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, false);
a->reset();
// Pressing mouse buttons is disabled since Enter is pressed
QVERIFY(!m.buttonPressed(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.buttonReleased(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
// Release Enter, so the system should be ready for new shortcuts
QVERIFY(!m.keyReleased(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, -1);
// Complete Ctrl+Shift+LB shortcut
QVERIFY(m.buttonPressed(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, 13);
QCOMPARE(a->m_ended, false);
QCOMPARE(a->m_beginNonNull, true);
QCOMPARE(a->m_endNonNull, false);
a->reset();
QVERIFY(m.buttonReleased(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, true);
a->reset();
// There is no Ctrl+Shift+RB shortcut
QVERIFY(!m.buttonPressed(Qt::RightButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.buttonReleased(Qt::RightButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
// Check that Ctrl+Shift+Enter is still enabled
QVERIFY(m.keyPressed(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, 11);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, false);
a->reset();
// Check autorepeat
QVERIFY(m.autoRepeatedKeyPressed(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, 11);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, false);
a->reset();
QVERIFY(!m.keyReleased(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, -1);
// Release Ctrl
QVERIFY(!m.keyReleased(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
// There is no Shift+LB shortcut
QVERIFY(!m.buttonPressed(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.buttonReleased(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
// But there *is* Shift+RB shortcut
QVERIFY(m.buttonPressed(Qt::RightButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, 12);
QCOMPARE(a->m_ended, false);
QCOMPARE(a->m_beginNonNull, true);
QCOMPARE(a->m_endNonNull, false);
a->reset();
QVERIFY(m.buttonReleased(Qt::RightButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, true);
a->reset();
// Check that Shift+Enter still works
QVERIFY(m.keyPressed(Qt::Key_Enter));
QCOMPARE(a->m_beginIndex, 10);
QCOMPARE(a->m_ended, true);
QCOMPARE(a->m_beginNonNull, false);
QCOMPARE(a->m_endNonNull, false);
a->reset();
m.leaveEvent();
}
void KisInputManagerTest::testReleaseUnnecessaryModifiers()
{
KisShortcutMatcher m;
m.enterEvent();
TestingAction *a = new TestingAction();
m.addShortcut(
createStrokeShortcut(a, 13,
QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
Qt::LeftButton));
// Test event with random values
QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(),
Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
// Press Ctrl+Shift
QVERIFY(!m.keyPressed(Qt::Key_Shift));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.keyPressed(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
// Complete Ctrl+Shift+LB shortcut
QVERIFY(m.buttonPressed(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, 13);
QCOMPARE(a->m_ended, false);
a->reset();
// Release Ctrl
QVERIFY(!m.keyReleased(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, false);
// Release Shift
QVERIFY(!m.keyReleased(Qt::Key_Shift));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, false);
// Release LB, now it should end
QVERIFY(m.buttonReleased(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, true);
a->reset();
m.leaveEvent();
}
void KisInputManagerTest::testMouseMoves()
{
KisShortcutMatcher m;
m.enterEvent();
TestingAction *a = new TestingAction();
m.addShortcut(
createStrokeShortcut(a, 13,
QList<Qt::Key>() << Qt::Key_Shift << Qt::Key_Control,
Qt::LeftButton));
// Test event with random values
QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(),
Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
// Press Ctrl+Shift
QVERIFY(!m.keyPressed(Qt::Key_Shift));
QCOMPARE(a->m_beginIndex, -1);
QVERIFY(!m.keyPressed(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
- QVERIFY(!m.mouseMoved(&mouseEvent));
+ QVERIFY(!m.cursorMoved(&mouseEvent));
QCOMPARE(a->m_gotInput, false);
// Complete Ctrl+Shift+LB shortcut
QVERIFY(m.buttonPressed(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, 13);
QCOMPARE(a->m_ended, false);
QCOMPARE(a->m_gotInput, false);
a->reset();
QVERIFY(m.mouseMoved(&mouseEvent));
QCOMPARE(a->m_gotInput, true);
a->reset();
// Release Ctrl
QVERIFY(!m.keyReleased(Qt::Key_Control));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, false);
QCOMPARE(a->m_gotInput, false);
// Release Shift
QVERIFY(!m.keyReleased(Qt::Key_Shift));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, false);
// Release LB, now it should end
QVERIFY(m.buttonReleased(Qt::LeftButton, &mouseEvent));
QCOMPARE(a->m_beginIndex, -1);
QCOMPARE(a->m_ended, true);
a->reset();
m.leaveEvent();
}
#include "../input/wintab/kis_incremental_average.h"
void KisInputManagerTest::testIncrementalAverage()
{
KisIncrementalAverage avg(3);
QCOMPARE(avg.pushThrough(10), 10);
QCOMPARE(avg.pushThrough(20), 13);
QCOMPARE(avg.pushThrough(30), 20);
QCOMPARE(avg.pushThrough(30), 26);
QCOMPARE(avg.pushThrough(30), 30);
}
QTEST_KDEMAIN(KisInputManagerTest, GUI)
diff --git a/krita/ui/tests/kis_model_index_converter_test.cpp b/krita/ui/tests/kis_model_index_converter_test.cpp
index 2ce9c8e7d93..78dc9b4247e 100644
--- a/krita/ui/tests/kis_model_index_converter_test.cpp
+++ b/krita/ui/tests/kis_model_index_converter_test.cpp
@@ -1,451 +1,451 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_model_index_converter_test.h"
#include <qtest_kde.h>
#include "kis_node_model.h"
#include "kis_dummies_facade.h"
#include "kis_node_dummies_graph.h"
#include "kis_model_index_converter.h"
#include "kis_model_index_converter_show_all.h"
void KisModelIndexConverterTest::init()
{
m_dummiesFacade = new KisDummiesFacade(0);
m_nodeModel = new KisNodeModel(0);
initBase();
constructImage();
addSelectionMasks();
m_dummiesFacade->setImage(m_image);
m_nodeModel->setDummiesFacade(m_dummiesFacade, m_image, 0);
}
void KisModelIndexConverterTest::cleanup()
{
m_nodeModel->setDummiesFacade(0, 0, 0);
m_dummiesFacade->setImage(0);
cleanupBase();
delete m_indexConverter;
delete m_nodeModel;
delete m_dummiesFacade;
}
inline void KisModelIndexConverterTest::checkIndexFromDummy(KisNodeSP node, int row) {
QModelIndex index;
KisNodeDummy *dummy;
dummy = m_dummiesFacade->dummyForNode(node);
index = m_indexConverter->indexFromDummy(dummy);
QVERIFY(index.isValid());
QCOMPARE(index.column(), 0);
QCOMPARE(index.row(), row);
QCOMPARE(m_indexConverter->dummyFromIndex(index), dummy);
}
inline void KisModelIndexConverterTest::checkInvalidIndexFromDummy(KisNodeSP node) {
QModelIndex index;
KisNodeDummy *dummy;
dummy = m_dummiesFacade->dummyForNode(node);
index = m_indexConverter->indexFromDummy(dummy);
QVERIFY(!index.isValid());
}
inline void KisModelIndexConverterTest::checkIndexFromAddedAllowedDummy(KisNodeSP parent, int index, int parentRow, int childRow, bool parentValid)
{
QString type = KisLayer::staticMetaObject.className();
checkIndexFromAddedDummy(parent, index, type, parentRow, childRow, parentValid);
}
inline void KisModelIndexConverterTest::checkIndexFromAddedDeniedDummy(KisNodeSP parent, int index, int parentRow, int childRow, bool parentValid)
{
QString type = KisSelectionMask::staticMetaObject.className();
checkIndexFromAddedDummy(parent, index, type, parentRow, childRow, parentValid);
}
inline void KisModelIndexConverterTest::checkIndexFromAddedDummy(KisNodeSP parent, int index, const QString &type, int parentRow, int childRow, bool parentValid)
{
QModelIndex modelIndex;
KisNodeDummy *dummy;
int row = 0;
bool result;
dummy = parent ? m_dummiesFacade->dummyForNode(parent) : 0;
result = m_indexConverter->indexFromAddedDummy(dummy, index, type, modelIndex, row);
- if(!result) qDebug() << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index;
+ if(!result) dbgKrita << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index;
QVERIFY(result);
QCOMPARE(modelIndex.isValid(), parentValid);
if(modelIndex.isValid()) {
QCOMPARE(modelIndex.row(), parentRow);
QCOMPARE(modelIndex.column(), 0);
}
- if(row != childRow) qDebug() << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index;
+ if(row != childRow) dbgKrita << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index;
QCOMPARE(row, childRow);
}
inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedAllowedDummy(KisNodeSP parent, int index)
{
QString type = KisLayer::staticMetaObject.className();
checkInvalidIndexFromAddedDummy(parent, index, type);
}
inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedDeniedDummy(KisNodeSP parent, int index)
{
QString type = KisSelectionMask::staticMetaObject.className();
checkInvalidIndexFromAddedDummy(parent, index, type);
}
inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedDummy(KisNodeSP parent, int index, const QString &type)
{
QModelIndex modelIndex;
KisNodeDummy *dummy;
int row = 0;
bool result;
dummy = parent ? m_dummiesFacade->dummyForNode(parent) : 0;
result = m_indexConverter->indexFromAddedDummy(dummy, index, type, modelIndex, row);
QVERIFY(!result);
}
inline void KisModelIndexConverterTest::checkDummyFromRow(KisNodeSP parent, int row, KisNodeSP expectedNode)
{
QModelIndex parentIndex;
KisNodeDummy *parentDummy;
if(parent) {
parentDummy = m_dummiesFacade->dummyForNode(parent);
parentIndex = m_indexConverter->indexFromDummy(parentDummy);
}
KisNodeDummy *resultDummy = m_indexConverter->dummyFromRow(row, parentIndex);
KisNodeSP resultNode = resultDummy ? resultDummy->node() : 0;
if(resultNode != expectedNode) {
- qDebug() << "Actual node: " << (resultNode ? resultNode->name() : "none");
- qDebug() << "Expected node:" << (expectedNode ? expectedNode->name() : "none");
+ dbgKrita << "Actual node: " << (resultNode ? resultNode->name() : "none");
+ dbgKrita << "Expected node:" << (expectedNode ? expectedNode->name() : "none");
QFAIL("Wrong node");
}
}
inline void KisModelIndexConverterTest::checkRowCount(KisNodeSP parent, int rowCount)
{
QModelIndex parentIndex;
KisNodeDummy *parentDummy;
if(parent) {
parentDummy = m_dummiesFacade->dummyForNode(parent);
parentIndex = m_indexConverter->indexFromDummy(parentDummy);
}
int resultRowCount = m_indexConverter->rowCount(parentIndex);
if(resultRowCount != rowCount) {
- qDebug() << "Wrong row count for:" << (parent ? parent->name() : "none");
- qDebug() << "Actual: " << resultRowCount;
- qDebug() << "Expected:" << rowCount;
+ dbgKrita << "Wrong row count for:" << (parent ? parent->name() : "none");
+ dbgKrita << "Actual: " << resultRowCount;
+ dbgKrita << "Expected:" << rowCount;
QFAIL("Wrong row count");
}
}
void KisModelIndexConverterTest::testIndexFromDummy()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false);
checkIndexFromDummy(m_layer1, 3);
checkIndexFromDummy(m_layer2, 2);
checkIndexFromDummy(m_layer3, 1);
checkIndexFromDummy(m_layer4, 0);
checkIndexFromDummy(m_mask1, 1);
checkIndexFromDummy(m_sel3, 0);
checkInvalidIndexFromDummy(m_image->root());
checkInvalidIndexFromDummy(m_sel1);
checkInvalidIndexFromDummy(m_sel2);
}
void KisModelIndexConverterTest::testIndexFromAddedAllowedDummy()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 4, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 4, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 3, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 2, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, false);
checkIndexFromAddedAllowedDummy(m_layer1, 0, 3, 0, true);
checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true);
checkInvalidIndexFromAddedAllowedDummy(0, 0);
}
void KisModelIndexConverterTest::testIndexFromAddedDeniedDummy()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 0);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 1);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 2);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 3);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 4);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 5);
checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 6);
checkIndexFromAddedDeniedDummy(m_layer1, 0, 3, 0, true);
checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true);
checkInvalidIndexFromAddedDeniedDummy(0, 0);
}
void KisModelIndexConverterTest::testDummyFromRow()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false);
checkDummyFromRow(m_image->root(), 0, m_layer4);
checkDummyFromRow(m_image->root(), 1, m_layer3);
checkDummyFromRow(m_image->root(), 2, m_layer2);
checkDummyFromRow(m_image->root(), 3, m_layer1);
checkDummyFromRow(0, 0, m_layer4);
checkDummyFromRow(0, 1, m_layer3);
checkDummyFromRow(0, 2, m_layer2);
checkDummyFromRow(0, 3, m_layer1);
checkDummyFromRow(m_layer3, 0, m_sel3);
checkDummyFromRow(m_layer3, 1, m_mask1);
}
void KisModelIndexConverterTest::testRowCount()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false);
checkRowCount(m_image->root(), 4);
checkRowCount(0, 4);
checkRowCount(m_layer1, 0);
checkRowCount(m_layer2, 0);
checkRowCount(m_layer3, 2);
checkRowCount(m_layer4, 0);
}
void KisModelIndexConverterTest::testIndexFromDummyShowGlobalSelection()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true);
checkIndexFromDummy(m_sel1, 5);
checkIndexFromDummy(m_layer1, 4);
checkIndexFromDummy(m_layer2, 3);
checkIndexFromDummy(m_sel2, 2);
checkIndexFromDummy(m_layer3, 1);
checkIndexFromDummy(m_layer4, 0);
checkIndexFromDummy(m_mask1, 1);
checkIndexFromDummy(m_sel3, 0);
checkInvalidIndexFromDummy(m_image->root());
}
void KisModelIndexConverterTest::testIndexFromAddedAllowedDummyShowGlobalSelection()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 6, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 5, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 4, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 3, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, false);
checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, false);
checkIndexFromAddedAllowedDummy(m_layer1, 0, 4, 0, true);
checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true);
checkInvalidIndexFromAddedAllowedDummy(0, 0);
}
void KisModelIndexConverterTest::testIndexFromAddedDeniedDummyShowGlobalSelection()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 0, 0, 6, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 1, 0, 5, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 2, 0, 4, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 3, 0, 3, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 4, 0, 2, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 5, 0, 1, false);
checkIndexFromAddedDeniedDummy(m_image->root(), 6, 0, 0, false);
checkIndexFromAddedDeniedDummy(m_layer1, 0, 4, 0, true);
checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true);
checkInvalidIndexFromAddedDeniedDummy(0, 0);
}
void KisModelIndexConverterTest::testDummyFromRowShowGlobalSelection()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true);
checkDummyFromRow(m_image->root(), 0, m_layer4);
checkDummyFromRow(m_image->root(), 1, m_layer3);
checkDummyFromRow(m_image->root(), 2, m_sel2);
checkDummyFromRow(m_image->root(), 3, m_layer2);
checkDummyFromRow(m_image->root(), 4, m_layer1);
checkDummyFromRow(m_image->root(), 5, m_sel1);
checkDummyFromRow(0, 0, m_layer4);
checkDummyFromRow(0, 1, m_layer3);
checkDummyFromRow(0, 2, m_sel2);
checkDummyFromRow(0, 3, m_layer2);
checkDummyFromRow(0, 4, m_layer1);
checkDummyFromRow(0, 5, m_sel1);
checkDummyFromRow(m_layer3, 0, m_sel3);
checkDummyFromRow(m_layer3, 1, m_mask1);
}
void KisModelIndexConverterTest::testRowCountShowGlobalSelection()
{
m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true);
checkRowCount(m_image->root(), 6);
checkRowCount(0, 6);
checkRowCount(m_layer1, 0);
checkRowCount(m_layer2, 0);
checkRowCount(m_layer3, 2);
checkRowCount(m_layer4, 0);
}
void KisModelIndexConverterTest::testIndexFromDummyShowAll()
{
m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel);
checkIndexFromDummy(m_sel1, 5);
checkIndexFromDummy(m_layer1, 4);
checkIndexFromDummy(m_layer2, 3);
checkIndexFromDummy(m_sel2, 2);
checkIndexFromDummy(m_layer3, 1);
checkIndexFromDummy(m_layer4, 0);
checkIndexFromDummy(m_mask1, 1);
checkIndexFromDummy(m_sel3, 0);
checkIndexFromDummy(m_image->root(), 0);
}
void KisModelIndexConverterTest::testIndexFromAddedAllowedDummyShowAll()
{
m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel);
checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 6, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 5, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 4, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 3, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, true);
checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, true);
checkIndexFromAddedAllowedDummy(m_layer1, 0, 4, 0, true);
checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true);
checkIndexFromAddedAllowedDummy(0, 0, 0, 0, false);
}
void KisModelIndexConverterTest::testIndexFromAddedDeniedDummyShowAll()
{
m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel);
checkIndexFromAddedDeniedDummy(m_image->root(), 0, 0, 6, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 1, 0, 5, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 2, 0, 4, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 3, 0, 3, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 4, 0, 2, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 5, 0, 1, true);
checkIndexFromAddedDeniedDummy(m_image->root(), 6, 0, 0, true);
checkIndexFromAddedDeniedDummy(m_layer1, 0, 4, 0, true);
checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true);
checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true);
checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true);
checkIndexFromAddedDeniedDummy(0, 0, 0, 0, false);
}
void KisModelIndexConverterTest::testDummyFromRowShowAll()
{
m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel);
checkDummyFromRow(m_image->root(), 0, m_layer4);
checkDummyFromRow(m_image->root(), 1, m_layer3);
checkDummyFromRow(m_image->root(), 2, m_sel2);
checkDummyFromRow(m_image->root(), 3, m_layer2);
checkDummyFromRow(m_image->root(), 4, m_layer1);
checkDummyFromRow(m_image->root(), 5, m_sel1);
checkDummyFromRow(0, 0, m_image->root());
checkDummyFromRow(m_layer3, 0, m_sel3);
checkDummyFromRow(m_layer3, 1, m_mask1);
}
void KisModelIndexConverterTest::testRowCountShowAll()
{
m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel);
checkRowCount(m_image->root(), 6);
checkRowCount(0, 1);
checkRowCount(m_layer1, 0);
checkRowCount(m_layer2, 0);
checkRowCount(m_layer3, 2);
checkRowCount(m_layer4, 0);
}
QTEST_KDEMAIN(KisModelIndexConverterTest, GUI)
diff --git a/krita/ui/tests/kis_selection_manager_test.cpp b/krita/ui/tests/kis_selection_manager_test.cpp
index 7c6c029b0c5..ef1ccd5f6a8 100644
--- a/krita/ui/tests/kis_selection_manager_test.cpp
+++ b/krita/ui/tests/kis_selection_manager_test.cpp
@@ -1,418 +1,418 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_selection_manager_test.h"
#include <operations/kis_operation_configuration.h>
#include <qtest_kde.h>
#include "ui_manager_test.h"
class SelectionManagerTester : public TestUtil::UiManagerTest
{
public:
SelectionManagerTester(bool useSelection)
: UiManagerTest(useSelection, false, "selection_manager_test")
{
Q_ASSERT(selectionManager);
}
};
void KisSelectionManagerTest::testFillForegroundWithoutSelection()
{
SelectionManagerTester t(false);
t.selectionManager->fillForegroundColor();
t.image->waitForDone();
QVERIFY(t.checkLayers("fill_foreground_without_selection"));
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->fillForegroundColor();
t.image->waitForDone();
QVERIFY(t.checkLayers("fill_foreground_without_selection"));
}
void KisSelectionManagerTest::testFillForegroundWithSelection()
{
SelectionManagerTester t(true);
t.selectionManager->fillForegroundColor();
t.image->waitForDone();
QVERIFY(t.checkLayers("fill_foreground_with_selection"));
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->fillForegroundColor();
t.image->waitForDone();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
QVERIFY(t.checkLayers("fill_foreground_with_selection"));
}
void KisSelectionManagerTest::testFillBackgroundWithSelection()
{
SelectionManagerTester t(true);
t.selectionManager->fillBackgroundColor();
t.image->waitForDone();
QVERIFY(t.checkLayers("fill_background_with_selection"));
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->fillBackgroundColor();
t.image->waitForDone();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
QVERIFY(t.checkLayers("fill_background_with_selection"));
}
void KisSelectionManagerTest::testFillPatternWithSelection()
{
SelectionManagerTester t(true);
t.selectionManager->fillPattern();
t.image->waitForDone();
QVERIFY(t.checkLayers("fill_pattern_with_selection"));
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->fillPattern();
t.image->waitForDone();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
QVERIFY(t.checkLayers("fill_pattern_with_selection"));
}
void KisSelectionManagerTest::testResizeToSelection()
{
SelectionManagerTester t(true);
t.selectionManager->imageResizeToSelection();
t.image->waitForDone();
QVERIFY(t.checkLayers("resize_to_selection"));
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->imageResizeToSelection();
t.image->waitForDone();
QEXPECT_FAIL("", "The user may run Resize to Selection concurrently. It will cause wrong image/selection size fetched for the crop. There is some barrier needed. At least it doesn't crash.", Continue);
QVERIFY(t.checkLayers("resize_to_selection"));
}
void KisSelectionManagerTest::testSelectAll()
{
SelectionManagerTester t(true);
t.selectionManager->selectAll();
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("select_all"));
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->selectAll();
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("select_all"));
}
void KisSelectionManagerTest::testDeselectReselect()
{
SelectionManagerTester t(true);
t.selectionManager->deselect();
t.image->waitForDone();
QVERIFY(t.checkNoSelection());
t.checkUndo();
t.startConcurrentTask();
t.selectionManager->deselect();
t.image->waitForDone();
QVERIFY(t.checkNoSelection());
t.selectionManager->reselect();
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("initial"));
t.undoStore->undo();
t.image->waitForDone();
QVERIFY(t.checkNoSelection());
t.startConcurrentTask();
t.selectionManager->reselect();
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("initial"));
}
void KisSelectionManagerTest::testCopyPaste()
{
SelectionManagerTester t(true);
t.selectionManager->copy();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayers("copy_paste"));
t.checkUndo();
t.startConcurrentTask();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.selectionManager->copy();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayers("copy_paste"));
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.checkUndo();
t.startConcurrentTask();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayers("copy_paste"));
}
void KisSelectionManagerTest::testCopyPasteMerged()
{
SelectionManagerTester t(true);
t.selectionManager->copyMerged();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayersFuzzy("copy_paste_merged"));
t.checkUndo();
t.startConcurrentTask();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.selectionManager->copyMerged();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayersFuzzy("copy_paste_merged"));
}
void KisSelectionManagerTest::testCutPaste()
{
SelectionManagerTester t(true);
t.selectionManager->cut();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayers("cut_paste"));
t.checkDoubleUndo();
t.startConcurrentTask();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
t.selectionManager->cut();
t.selectionManager->paste();
t.image->waitForDone();
QVERIFY(t.checkLayers("cut_paste"));
}
void KisSelectionManagerTest::testInvertSelection()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("invertselection");
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkLayers("invert_selection"));
t.checkUndo();
t.startConcurrentTask();
QEXPECT_FAIL("", "Fix some race condition on clone layers!", Continue);
config = new KisOperationConfiguration("invertselection");
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkLayers("invert_selection"));
}
void KisSelectionManagerTest::testFeatherSelection()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("featherselection");
config->setProperty("radius", 10);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("feather_selection"));
t.checkUndo();
t.startConcurrentTask();
config = new KisOperationConfiguration("featherselection");
config->setProperty("radius", 10);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("feather_selection"));
}
void KisSelectionManagerTest::testGrowSelectionSimplified()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("growselection");
config->setProperty("x-radius", 10);
config->setProperty("y-radius", 5);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("grow_selection"));
}
void KisSelectionManagerTest::testShrinkSelectionUnlockedSimplified()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("shrinkselection");
config->setProperty("x-radius", 10);
config->setProperty("y-radius", 5);
config->setProperty("edgeLock", false);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("shrink_selection_unlocked"));
}
void KisSelectionManagerTest::testShrinkSelectionLockedSimplified()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("shrinkselection");
config->setProperty("x-radius", 10);
config->setProperty("y-radius", 5);
config->setProperty("edgeLock", true);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("shrink_selection_locked"));
}
void KisSelectionManagerTest::testSmoothSelectionSimplified()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("smoothselection");
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("smooth_selection"));
}
void KisSelectionManagerTest::testErodeSelectionSimplified()
{
// SelectionManagerTester t(true);
//
// t.selectionManager->erode();
// t.image->waitForDone();
// QVERIFY(t.checkSelectionOnly("erode_selection"));
}
void KisSelectionManagerTest::testDilateSelectionSimplified()
{
// SelectionManagerTester t(true);
//
// t.selectionManager->dilate();
// t.image->waitForDone();
// QVERIFY(t.checkSelectionOnly("dilate_selection"));
}
void KisSelectionManagerTest::testBorderSelectionSimplified()
{
SelectionManagerTester t(true);
KisOperationConfiguration* config = new KisOperationConfiguration("borderselection");
config->setProperty("x-radius", 10);
config->setProperty("y-radius", 5);
t.actionManager->runOperationFromConfiguration(config);
t.image->waitForDone();
QVERIFY(t.checkSelectionOnly("border_selection"));
}
#include <floodfill/kis_scanline_fill.h>
void KisSelectionManagerTest::testScanline16bit()
{
const int THRESHOLD = 20;
QString fileName = TestUtil::fetchDataFileLazy("flood_fill_16bit.kra");
QVERIFY(QFile::exists(fileName));
KisDocument *doc = KisPart::instance()->createDocument();
doc->loadNativeFormat(fileName);
KisPaintDeviceSP dev = doc->image()->root()->firstChild()->paintDevice();
QVERIFY(dev);
- qDebug() << ppVar(dev->colorSpace());
+ dbgKrita << ppVar(dev->colorSpace());
QRect imageRect = doc->image()->bounds();
- qDebug() << ppVar(imageRect);
+ dbgKrita << ppVar(imageRect);
QPoint startPoint = imageRect.center();
- qDebug() << ppVar(startPoint);
+ dbgKrita << ppVar(startPoint);
KisPixelSelectionSP pixelSelection = new KisPixelSelection();
{
KisScanlineFill gc(dev, startPoint, imageRect);
gc.setThreshold(THRESHOLD);
gc.fillSelection(pixelSelection);
QImage resultImage =
pixelSelection->convertToQImage(0,
imageRect);
QVERIFY(TestUtil::checkQImage(resultImage,
"selection_manager_test",
"scanline",
"16bit_thres_20"));
}
const KoColorSpace *rgb8CS = KoColorSpaceRegistry::instance()->rgb8();
pixelSelection->clear();
dev->convertTo(rgb8CS);
{
KisScanlineFill gc(dev, startPoint, imageRect);
gc.setThreshold(THRESHOLD);
gc.fillSelection(pixelSelection);
QImage resultImage =
pixelSelection->convertToQImage(0,
imageRect);
QVERIFY(TestUtil::checkQImage(resultImage,
"selection_manager_test",
"scanline",
"8bit_thres_20"));
}
}
QTEST_KDEMAIN(KisSelectionManagerTest, GUI)
diff --git a/krita/ui/tests/kis_zoom_and_pan_test.cpp b/krita/ui/tests/kis_zoom_and_pan_test.cpp
index a89fe8c7cf4..63e1f2be845 100644
--- a/krita/ui/tests/kis_zoom_and_pan_test.cpp
+++ b/krita/ui/tests/kis_zoom_and_pan_test.cpp
@@ -1,764 +1,764 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_zoom_and_pan_test.h"
#include <cmath>
#include <qtest_kde.h>
#include "testutil.h"
#include "qimage_based_test.h"
#include <kactioncollection.h>
#include "kis_config.h"
#include "KisMainWindow.h"
#include "KoZoomController.h"
#include "KisDocument.h"
#include "KisPart.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_coordinates_converter.h"
#include "kis_filter_strategy.h"
class ZoomAndPanTester : public TestUtil::QImageBasedTest
{
public:
ZoomAndPanTester()
// we are not going to use our own QImage sets,so
// just exploit the set of the selection manager test
: QImageBasedTest("selection_manager_test")
{
m_undoStore = new KisSurrogateUndoStore();
m_image = createImage(m_undoStore);
m_image->initialRefreshGraph();
QVERIFY(checkLayersInitial(m_image));
m_doc = KisPart::instance()->createDocument();
m_doc->setCurrentImage(m_image);
m_mainWindow = KisPart::instance()->createMainWindow();
m_view = new KisView(m_doc, m_mainWindow->resourceManager(), m_mainWindow->actionCollection(), m_mainWindow);
m_image->refreshGraph();
m_mainWindow->show();
}
~ZoomAndPanTester() {
m_image->waitForDone();
QApplication::processEvents();
delete m_mainWindow;
delete m_doc;
/**
* The event queue may have up to 200k events
* by the time all the tests are finished. Removing
* all of them may last forever, so clear them after
* every single test is finished
*/
QApplication::removePostedEvents(0);
}
QPointer<KisView> view() {
return m_view;
}
KisMainWindow* mainWindow() {
return m_mainWindow;
}
KisImageWSP image() {
return m_image;
}
KisCanvas2* canvas() {
return m_view->canvasBase();
}
QWidget* canvasWidget() {
return m_view->canvasBase()->canvasWidget();
}
KoZoomController* zoomController() {
return m_view->zoomController();
}
KisCanvasController* canvasController() {
return dynamic_cast<KisCanvasController*>(m_view->canvasController());
}
const KisCoordinatesConverter* coordinatesConverter() {
return m_view->canvasBase()->coordinatesConverter();
}
private:
KisSurrogateUndoStore *m_undoStore;
KisImageSP m_image;
KisDocument *m_doc;
QPointer<KisView>m_view;
KisMainWindow *m_mainWindow;
};
template<class P, class T>
inline bool compareWithRounding(const P &pt0, const P &pt1, T tolerance)
{
return qAbs(pt0.x() - pt1.x()) <= tolerance &&
qAbs(pt0.y() - pt1.y()) <= tolerance;
}
bool verifyOffset(ZoomAndPanTester &t, const QPoint &offset) {
if (t.coordinatesConverter()->documentOffset() != offset) {
- qDebug() << "########################";
- qDebug() << "Expected Offset:" << offset;
- qDebug() << "Actual values:";
- qDebug() << "Offset:" << t.coordinatesConverter()->documentOffset();
- qDebug() << "wsize:" << t.canvasWidget()->size();
- qDebug() << "vport:" << t.canvasController()->viewportSize();
- qDebug() << "pref:" << t.canvasController()->preferredCenter();
- qDebug() << "########################";
+ dbgKrita << "########################";
+ dbgKrita << "Expected Offset:" << offset;
+ dbgKrita << "Actual values:";
+ dbgKrita << "Offset:" << t.coordinatesConverter()->documentOffset();
+ dbgKrita << "wsize:" << t.canvasWidget()->size();
+ dbgKrita << "vport:" << t.canvasController()->viewportSize();
+ dbgKrita << "pref:" << t.canvasController()->preferredCenter();
+ dbgKrita << "########################";
}
return t.coordinatesConverter()->documentOffset() == offset;
}
bool KisZoomAndPanTest::checkPan(ZoomAndPanTester &t, QPoint shift)
{
QPoint oldOffset = t.coordinatesConverter()->documentOffset();
QPointF oldPrefCenter = t.canvasController()->preferredCenter();
t.canvasController()->pan(shift);
QPoint newOffset = t.coordinatesConverter()->documentOffset();
QPointF newPrefCenter = t.canvasController()->preferredCenter();
QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft();
QPoint expectedOffset = oldOffset + shift;
QPointF expectedPrefCenter = oldPrefCenter + shift;
// no tolerance accepted for pan
bool offsetAsExpected = newOffset == expectedOffset;
// rounding can happen due to the scroll bars being the main
// source of the offset
bool preferredCenterAsExpected =
compareWithRounding(expectedPrefCenter, newPrefCenter, 1.0);
bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset;
if (!offsetAsExpected ||
!preferredCenterAsExpected ||
!topLeftAsExpected) {
- qDebug() << "***** PAN *****************";
+ dbgKrita << "***** PAN *****************";
if(!offsetAsExpected) {
- qDebug() << " ### Offset invariant broken";
+ dbgKrita << " ### Offset invariant broken";
}
if(!preferredCenterAsExpected) {
- qDebug() << " ### Preferred center invariant broken";
+ dbgKrita << " ### Preferred center invariant broken";
}
if(!topLeftAsExpected) {
- qDebug() << " ### TopLeft invariant broken";
+ dbgKrita << " ### TopLeft invariant broken";
}
- qDebug() << ppVar(expectedOffset);
- qDebug() << ppVar(expectedPrefCenter);
- qDebug() << ppVar(oldOffset) << ppVar(newOffset);
- qDebug() << ppVar(oldPrefCenter) << ppVar(newPrefCenter);
- qDebug() << ppVar(newTopLeft);
- qDebug() << "***************************";
+ dbgKrita << ppVar(expectedOffset);
+ dbgKrita << ppVar(expectedPrefCenter);
+ dbgKrita << ppVar(oldOffset) << ppVar(newOffset);
+ dbgKrita << ppVar(oldPrefCenter) << ppVar(newPrefCenter);
+ dbgKrita << ppVar(newTopLeft);
+ dbgKrita << "***************************";
}
return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected;
}
bool KisZoomAndPanTest::checkInvariants(const QPointF &baseFlakePoint,
const QPoint &oldOffset,
const QPointF &oldPreferredCenter,
qreal oldZoom,
const QPoint &newOffset,
const QPointF &newPreferredCenter,
qreal newZoom,
const QPointF &newTopLeft,
const QSize &oldDocumentSize)
{
qreal k = newZoom / oldZoom;
QPointF expectedOffset = oldOffset + (k - 1) * baseFlakePoint;
QPointF expectedPreferredCenter = oldPreferredCenter + (k - 1) * baseFlakePoint;
qreal oldPreferredCenterFractionX = 1.0 * oldPreferredCenter.x() / oldDocumentSize.width();
qreal oldPreferredCenterFractionY = 1.0 * oldPreferredCenter.y() / oldDocumentSize.height();
qreal roundingTolerance =
qMax(qreal(1.0), qMax(oldPreferredCenterFractionX, oldPreferredCenterFractionY) / k);
/**
* In the computation of the offset two roundings happen:
* first for the computation of oldOffset and the second
* for the computation of newOffset. So the maximum tolerance
* should equal 2.
*/
bool offsetAsExpected =
compareWithRounding(expectedOffset, QPointF(newOffset), 2 * roundingTolerance);
/**
* Rounding for the preferred center happens due to the rounding
* of the document size while zooming. The wider the step of the
* zooming, the bigger tolerance should be
*/
bool preferredCenterAsExpected =
compareWithRounding(expectedPreferredCenter, newPreferredCenter,
roundingTolerance);
bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset;
if (!offsetAsExpected ||
!preferredCenterAsExpected ||
!topLeftAsExpected) {
- qDebug() << "***** ZOOM ****************";
+ dbgKrita << "***** ZOOM ****************";
if(!offsetAsExpected) {
- qDebug() << " ### Offset invariant broken";
+ dbgKrita << " ### Offset invariant broken";
}
if(!preferredCenterAsExpected) {
- qDebug() << " ### Preferred center invariant broken";
+ dbgKrita << " ### Preferred center invariant broken";
}
if(!topLeftAsExpected) {
- qDebug() << " ### TopLeft invariant broken";
+ dbgKrita << " ### TopLeft invariant broken";
}
- qDebug() << ppVar(expectedOffset);
- qDebug() << ppVar(expectedPreferredCenter);
- qDebug() << ppVar(oldOffset) << ppVar(newOffset);
- qDebug() << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
- qDebug() << ppVar(oldPreferredCenterFractionX);
- qDebug() << ppVar(oldPreferredCenterFractionY);
- qDebug() << ppVar(oldZoom) << ppVar(newZoom);
- qDebug() << ppVar(baseFlakePoint);
- qDebug() << ppVar(newTopLeft);
- qDebug() << ppVar(roundingTolerance);
- qDebug() << "***************************";
+ dbgKrita << ppVar(expectedOffset);
+ dbgKrita << ppVar(expectedPreferredCenter);
+ dbgKrita << ppVar(oldOffset) << ppVar(newOffset);
+ dbgKrita << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
+ dbgKrita << ppVar(oldPreferredCenterFractionX);
+ dbgKrita << ppVar(oldPreferredCenterFractionY);
+ dbgKrita << ppVar(oldZoom) << ppVar(newZoom);
+ dbgKrita << ppVar(baseFlakePoint);
+ dbgKrita << ppVar(newTopLeft);
+ dbgKrita << ppVar(roundingTolerance);
+ dbgKrita << "***************************";
}
return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected;
}
bool KisZoomAndPanTest::checkZoomWithAction(ZoomAndPanTester &t, qreal newZoom, bool limitedZoom)
{
QPoint oldOffset = t.coordinatesConverter()->documentOffset();
QPointF oldPrefCenter = t.canvasController()->preferredCenter();
qreal oldZoom = t.zoomController()->zoomAction()->effectiveZoom();
QSize oldDocumentSize = t.canvasController()->documentSize();
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, newZoom);
QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft();
return checkInvariants(oldPrefCenter,
oldOffset,
oldPrefCenter,
oldZoom,
t.coordinatesConverter()->documentOffset(),
t.canvasController()->preferredCenter(),
limitedZoom ? oldZoom : newZoom,
newTopLeft,
oldDocumentSize);
}
bool KisZoomAndPanTest::checkZoomWithWheel(ZoomAndPanTester &t, const QPoint &widgetPoint, qreal zoomCoeff, bool limitedZoom)
{
QPoint oldOffset = t.coordinatesConverter()->documentOffset();
QPointF oldPrefCenter = t.canvasController()->preferredCenter();
qreal oldZoom = t.zoomController()->zoomAction()->effectiveZoom();
QSize oldDocumentSize = t.canvasController()->documentSize();
t.canvasController()->zoomRelativeToPoint(widgetPoint, zoomCoeff);
QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft();
return checkInvariants(oldOffset + widgetPoint,
oldOffset,
oldPrefCenter,
oldZoom,
t.coordinatesConverter()->documentOffset(),
t.canvasController()->preferredCenter(),
limitedZoom ? oldZoom : zoomCoeff * oldZoom,
newTopLeft,
oldDocumentSize);
}
void KisZoomAndPanTest::testZoom100ChangingWidgetSize()
{
ZoomAndPanTester t;
QCOMPARE(t.image()->size(), QSize(640,441));
QCOMPARE(t.image()->xRes(), 1.0);
QCOMPARE(t.image()->yRes(), 1.0);
t.canvasController()->resize(QSize(1000,1000));
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
t.canvasController()->setPreferredCenter(QPoint(320,220));
QCOMPARE(t.canvasWidget()->size(), QSize(983,983));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QVERIFY(verifyOffset(t, QPoint(-171,-271)));
t.canvasController()->resize(QSize(700,700));
QCOMPARE(t.canvasWidget()->size(), QSize(683,683));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QVERIFY(verifyOffset(t, QPoint(-171,-271)));
t.canvasController()->setPreferredCenter(QPoint(320,220));
QVERIFY(verifyOffset(t, QPoint(-21,-121)));
t.canvasController()->resize(QSize(400,400));
QCOMPARE(t.canvasWidget()->size(), QSize(383,383));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QVERIFY(verifyOffset(t, QPoint(-21,-121)));
t.canvasController()->setPreferredCenter(QPoint(320,220));
QVERIFY(verifyOffset(t, QPoint(129,29)));
t.canvasController()->pan(QPoint(100,100));
QVERIFY(verifyOffset(t, QPoint(229,129)));
}
void KisZoomAndPanTest::initializeViewport(ZoomAndPanTester &t, bool fullscreenMode, bool rotate, bool mirror)
{
QCOMPARE(t.image()->size(), QSize(640,441));
QCOMPARE(t.image()->xRes(), 1.0);
QCOMPARE(t.image()->yRes(), 1.0);
t.canvasController()->resize(QSize(500,500));
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0);
t.canvasController()->setPreferredCenter(QPoint(320,220));
QCOMPARE(t.canvasWidget()->size(), QSize(483,483));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QVERIFY(verifyOffset(t, QPoint(79,-21)));
if (fullscreenMode) {
QCOMPARE(t.canvasController()->preferredCenter(), QPointF(320,220));
QAction *action = t.view()->viewManager()->actionCollection()->action("view_show_just_the_canvas");
action->setChecked(true);
QVERIFY(verifyOffset(t, QPoint(79,-21)));
QCOMPARE(t.canvasController()->preferredCenter(), QPointF(329,220));
t.canvasController()->resize(QSize(483,483));
QCOMPARE(t.canvasWidget()->size(), QSize(483,483));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QVERIFY(verifyOffset(t, QPoint(79,-21)));
/**
* FIXME: here is a small flaw in KoCanvasControllerWidget
* We cannot set the center point explicitly, because it'll be rounded
* up by recenterPreferred function, so real center point will be
* different. Make the preferredCenter() return real center of the
* image instead of the set value
*/
QCOMPARE(t.canvasController()->preferredCenter(), QPointF(320.5,220));
}
if (rotate) {
t.canvasController()->rotateCanvas(90);
QVERIFY(verifyOffset(t, QPoint(-21,79)));
QVERIFY(compareWithRounding(QPointF(220,320), t.canvasController()->preferredCenter(), 2));
QCOMPARE(t.coordinatesConverter()->imageRectInWidgetPixels().topLeft().toPoint(), -t.coordinatesConverter()->documentOffset());
}
if (mirror) {
t.canvasController()->mirrorCanvas(true);
QVERIFY(verifyOffset(t, QPoint(78, -21)));
QVERIFY(compareWithRounding(QPointF(320,220), t.canvasController()->preferredCenter(), 2));
QCOMPARE(t.coordinatesConverter()->imageRectInWidgetPixels().topLeft().toPoint(), -t.coordinatesConverter()->documentOffset());
}
}
void KisZoomAndPanTest::testSequentialActionZoomAndPan(bool fullscreenMode, bool rotate, bool mirror)
{
ZoomAndPanTester t;
initializeViewport(t, fullscreenMode, rotate, mirror);
QVERIFY(checkZoomWithAction(t, 0.5));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithAction(t, 0.25));
QVERIFY(checkPan(t, QPoint(-100,-100)));
QVERIFY(checkZoomWithAction(t, 0.35));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithAction(t, 0.45));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithAction(t, 0.85));
QVERIFY(checkPan(t, QPoint(-100,-100)));
QVERIFY(checkZoomWithAction(t, 2.35));
QVERIFY(checkPan(t, QPoint(100,100)));
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPan(bool fullscreenMode, bool rotate, bool mirror)
{
ZoomAndPanTester t;
initializeViewport(t, fullscreenMode, rotate, mirror);
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5));
QVERIFY(checkPan(t, QPoint(-100,-100)));
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 1.25));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 1.5));
QVERIFY(checkPan(t, QPoint(100,100)));
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 2.5));
QVERIFY(checkPan(t, QPoint(-100,-100)));
// check one point which is outside the widget
QVERIFY(checkZoomWithWheel(t, QPoint(-100,100), 2.5));
QVERIFY(checkPan(t, QPoint(-100,-100)));
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5));
QVERIFY(checkPan(t, QPoint(-100,-100)));
}
void KisZoomAndPanTest::testSequentialActionZoomAndPan()
{
testSequentialActionZoomAndPan(false, false, false);
}
void KisZoomAndPanTest::testSequentialActionZoomAndPanFullscreen()
{
testSequentialActionZoomAndPan(true, false, false);
}
void KisZoomAndPanTest::testSequentialActionZoomAndPanRotate()
{
testSequentialActionZoomAndPan(false, true, false);
}
void KisZoomAndPanTest::testSequentialActionZoomAndPanRotateFullscreen()
{
testSequentialActionZoomAndPan(true, true, false);
}
void KisZoomAndPanTest::testSequentialActionZoomAndPanMirror()
{
testSequentialActionZoomAndPan(false, false, true);
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPan()
{
testSequentialWheelZoomAndPan(false, false, false);
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPanFullscreen()
{
testSequentialWheelZoomAndPan(true, false, false);
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPanRotate()
{
testSequentialWheelZoomAndPan(false, true, false);
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPanRotateFullscreen()
{
testSequentialWheelZoomAndPan(true, true, false);
}
void KisZoomAndPanTest::testSequentialWheelZoomAndPanMirror()
{
testSequentialWheelZoomAndPan(false, false, true);
}
void KisZoomAndPanTest::testZoomOnBorderZoomLevels()
{
ZoomAndPanTester t;
initializeViewport(t, false, false, false);
QPoint widgetPoint(100,100);
- qWarning() << "WARNING: testZoomOnBorderZoomLevels() is disabled due to some changes in KoZoomMode::minimum/maximumZoom()";
+ warnKrita << "WARNING: testZoomOnBorderZoomLevels() is disabled due to some changes in KoZoomMode::minimum/maximumZoom()";
return;
// test min zoom level
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, KoZoomMode::minimumZoom());
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5, true));
QVERIFY(checkZoomWithAction(t, KoZoomMode::minimumZoom() * 0.5, true));
// test max zoom level
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, KoZoomMode::maximumZoom());
QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 2.0, true));
QVERIFY(checkZoomWithAction(t, KoZoomMode::maximumZoom() * 2.0, true));
}
inline QTransform correctionMatrix(qreal angle)
{
return QTransform(0,0,0,sin(M_PI * angle / 180),0,0,0,0,1);
}
bool KisZoomAndPanTest::checkRotation(ZoomAndPanTester &t, qreal angle)
{
// save old values
QPoint oldOffset = t.coordinatesConverter()->documentOffset();
QPointF oldCenteringCorrection = t.coordinatesConverter()->centeringCorrection();
QPointF oldPreferredCenter = t.canvasController()->preferredCenter();
QPointF oldRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
QSize oldDocumentSize = t.canvasController()->documentSize();
qreal baseAngle = t.coordinatesConverter()->rotationAngle();
t.canvasController()->rotateCanvas(angle);
// save result values
QPoint newOffset = t.coordinatesConverter()->documentOffset();
QPointF newCenteringCorrection = t.coordinatesConverter()->centeringCorrection();
QPointF newPreferredCenter = t.canvasController()->preferredCenter();
QPointF newRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
QSize newDocumentSize = t.canvasController()->documentSize();
// calculate theoretical preferred center
QTransform rot;
rot.rotate(angle);
QSizeF dSize = t.coordinatesConverter()->imageSizeInFlakePixels();
QPointF dPoint(dSize.width(), dSize.height());
QPointF expectedPreferredCenter =
(oldPreferredCenter - dPoint * correctionMatrix(baseAngle)) * rot +
dPoint * correctionMatrix(baseAngle + angle);
// calculate theoretical offset based on the real preferred center
QPointF wPoint(t.canvasWidget()->size().width(), t.canvasWidget()->size().height());
QPointF expectedOldOffset = oldPreferredCenter - 0.5 * wPoint;
QPointF expectedNewOffset = newPreferredCenter - 0.5 * wPoint;
bool preferredCenterAsExpected =
compareWithRounding(expectedPreferredCenter, newPreferredCenter, 2);
bool oldOffsetAsExpected =
compareWithRounding(expectedOldOffset + oldCenteringCorrection, QPointF(oldOffset), 2);
bool newOffsetAsExpected =
compareWithRounding(expectedNewOffset + newCenteringCorrection, QPointF(newOffset), 3);
qreal zoom = t.zoomController()->zoomAction()->effectiveZoom();
bool realCenterPointAsExpected =
compareWithRounding(oldRealCenterPoint, newRealCenterPoint, 2/zoom);
if (!oldOffsetAsExpected ||
!newOffsetAsExpected ||
!preferredCenterAsExpected ||
!realCenterPointAsExpected) {
- qDebug() << "***** ROTATE **************";
+ dbgKrita << "***** ROTATE **************";
if(!oldOffsetAsExpected) {
- qDebug() << " ### Old offset invariant broken";
+ dbgKrita << " ### Old offset invariant broken";
}
if(!newOffsetAsExpected) {
- qDebug() << " ### New offset invariant broken";
+ dbgKrita << " ### New offset invariant broken";
}
if(!preferredCenterAsExpected) {
- qDebug() << " ### Preferred center invariant broken";
+ dbgKrita << " ### Preferred center invariant broken";
}
if(!realCenterPointAsExpected) {
- qDebug() << " ### *Real* center invariant broken";
+ dbgKrita << " ### *Real* center invariant broken";
}
- qDebug() << ppVar(expectedOldOffset);
- qDebug() << ppVar(expectedNewOffset);
- qDebug() << ppVar(expectedPreferredCenter);
- qDebug() << ppVar(oldOffset) << ppVar(newOffset);
- qDebug() << ppVar(oldCenteringCorrection) << ppVar(newCenteringCorrection);
- qDebug() << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
- qDebug() << ppVar(oldRealCenterPoint) << ppVar(newRealCenterPoint);
- qDebug() << ppVar(oldDocumentSize) << ppVar(newDocumentSize);
- qDebug() << ppVar(baseAngle) << "deg";
- qDebug() << ppVar(angle) << "deg";
- qDebug() << "***************************";
+ dbgKrita << ppVar(expectedOldOffset);
+ dbgKrita << ppVar(expectedNewOffset);
+ dbgKrita << ppVar(expectedPreferredCenter);
+ dbgKrita << ppVar(oldOffset) << ppVar(newOffset);
+ dbgKrita << ppVar(oldCenteringCorrection) << ppVar(newCenteringCorrection);
+ dbgKrita << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter);
+ dbgKrita << ppVar(oldRealCenterPoint) << ppVar(newRealCenterPoint);
+ dbgKrita << ppVar(oldDocumentSize) << ppVar(newDocumentSize);
+ dbgKrita << ppVar(baseAngle) << "deg";
+ dbgKrita << ppVar(angle) << "deg";
+ dbgKrita << "***************************";
}
return preferredCenterAsExpected && oldOffsetAsExpected && newOffsetAsExpected && realCenterPointAsExpected;
}
void KisZoomAndPanTest::testRotation(qreal vastScrolling, qreal zoom)
{
KisConfig cfg;
cfg.setVastScrolling(vastScrolling);
ZoomAndPanTester t;
QCOMPARE(t.image()->size(), QSize(640,441));
QCOMPARE(t.image()->xRes(), 1.0);
QCOMPARE(t.image()->yRes(), 1.0);
QPointF preferredCenter = zoom * t.image()->bounds().center();
t.canvasController()->resize(QSize(500,500));
t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom);
t.canvasController()->setPreferredCenter(preferredCenter.toPoint());
QCOMPARE(t.canvasWidget()->size(), QSize(483,483));
QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize());
QPointF realCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint());
QPointF expectedCenterPoint = QPointF(t.image()->bounds().center());
if(!compareWithRounding(realCenterPoint, expectedCenterPoint, 2/zoom)) {
- qDebug() << "Failed to set initial center point";
- qDebug() << ppVar(expectedCenterPoint) << ppVar(realCenterPoint);
+ dbgKrita << "Failed to set initial center point";
+ dbgKrita << ppVar(expectedCenterPoint) << ppVar(realCenterPoint);
QFAIL("FAIL: Failed to set initial center point");
}
QVERIFY(checkRotation(t, 30));
QVERIFY(checkRotation(t, 20));
QVERIFY(checkRotation(t, 10));
QVERIFY(checkRotation(t, 5));
QVERIFY(checkRotation(t, 5));
QVERIFY(checkRotation(t, 5));
if(vastScrolling < 0.5 && zoom < 1) {
- qWarning() << "Disabling a few tests for vast scrolling ="
+ warnKrita << "Disabling a few tests for vast scrolling ="
<< vastScrolling << ". See comment for more";
/**
* We have to disable a couple of tests here for the case when
* vastScrolling value is 0.2. The problem is that the centering
* correction applied to the offset in
* KisCanvasController::rotateCanvas pollutes the preferredCenter
* value, because KoCnvasControllerWidget has no access to this
* correction and cannot calculate the real value of the center of
* the image. To fix this bug the calculation of correction
* (aka "origin") should be moved to the KoCanvasControllerWidget
* itself which would cause quite huge changes (including the change
* of the external interface of it). Namely, we would have to
* *calculate* offset from the value of the scroll bars, but not
* use their values directly:
*
* offset = scrollBarValue - origin
*
* So now we just disable these unittests and allow a couple
* of "jumping" bugs appear in vastScrolling < 0.5 modes, which
* is, actually, not the default case.
*/
} else {
QVERIFY(checkRotation(t, 5));
QVERIFY(checkRotation(t, 5));
QVERIFY(checkRotation(t, 5));
}
}
void KisZoomAndPanTest::testRotation_VastScrolling_1_0()
{
testRotation(0.9, 1.0);
}
void KisZoomAndPanTest::testRotation_VastScrolling_0_5()
{
testRotation(0.9, 0.5);
}
void KisZoomAndPanTest::testRotation_NoVastScrolling_1_0()
{
testRotation(0.2, 1.0);
}
void KisZoomAndPanTest::testRotation_NoVastScrolling_0_5()
{
testRotation(0.2, 0.5);
}
void KisZoomAndPanTest::testImageRescaled_0_5()
{
ZoomAndPanTester t;
QApplication::processEvents();
initializeViewport(t, false, false, false);
QApplication::processEvents();
QVERIFY(checkPan(t, QPoint(200,200)));
QApplication::processEvents();
QPointF oldStillPoint =
t.coordinatesConverter()->imageRectInWidgetPixels().center();
KisFilterStrategy *strategy = new KisBilinearFilterStrategy();
t.image()->scaleImage(QSize(320, 220), t.image()->xRes(), t.image()->yRes(), strategy);
t.image()->waitForDone();
QApplication::processEvents();
delete strategy;
QPointF newStillPoint =
t.coordinatesConverter()->imageRectInWidgetPixels().center();
QVERIFY(compareWithRounding(oldStillPoint, newStillPoint, 1.0));
}
void KisZoomAndPanTest::testImageCropped()
{
ZoomAndPanTester t;
QApplication::processEvents();
initializeViewport(t, false, false, false);
QApplication::processEvents();
QVERIFY(checkPan(t, QPoint(-150,-150)));
QApplication::processEvents();
QPointF oldStillPoint =
t.coordinatesConverter()->imageToWidget(QPointF(150,150));
t.image()->cropImage(QRect(100,100,100,100));
t.image()->waitForDone();
QApplication::processEvents();
QPointF newStillPoint =
t.coordinatesConverter()->imageToWidget(QPointF(50,50));
QVERIFY(compareWithRounding(oldStillPoint, newStillPoint, 1.0));
}
QTEST_KDEMAIN(KisZoomAndPanTest, GUI)
diff --git a/krita/ui/tests/modeltest.cpp b/krita/ui/tests/modeltest.cpp
index bb6b1598c22..9db9840c50c 100644
--- a/krita/ui/tests/modeltest.cpp
+++ b/krita/ui/tests/modeltest.cpp
@@ -1,545 +1,545 @@
/****************************************************************************
**
** Copyright (C) 2007 Trolltech ASA. All rights reserved.
**
** This file is part of the Qt Concurrent project on Trolltech Labs.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
#include <QtGui>
#include "modeltest.h"
Q_DECLARE_METATYPE(QModelIndex)
/*!
Connect to all of the models signals. Whenever anything happens recheck everything.
*/
ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false)
{
Q_ASSERT(model);
connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests()));
connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests()));
connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(runAllTests()));
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(runAllTests()));
// Special checks for inserting/removing
connect(model, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(layoutAboutToBeChanged()));
connect(model, SIGNAL(layoutChanged()),
this, SLOT(layoutChanged()));
connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)));
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(rowsInserted(QModelIndex,int,int)));
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(rowsRemoved(QModelIndex,int,int)));
runAllTests();
}
void ModelTest::runAllTests()
{
if (fetchingMore)
return;
nonDestructiveBasicTest();
rowCount();
columnCount();
hasIndex();
index();
parent();
data();
}
/*!
nonDestructiveBasicTest tries to call a number of the basic functions (not all)
to make sure the model doesn't outright segfault, testing the functions that makes sense.
*/
void ModelTest::nonDestructiveBasicTest()
{
Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex());
model->canFetchMore(QModelIndex());
Q_ASSERT(model->columnCount(QModelIndex()) >= 0);
Q_ASSERT(model->data(QModelIndex()) == QVariant());
fetchingMore = true;
model->fetchMore(QModelIndex());
fetchingMore = false;
Qt::ItemFlags flags = model->flags(QModelIndex());
Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0);
Q_UNUSED(flags);
model->hasChildren(QModelIndex());
model->hasIndex(0, 0);
model->headerData(0, Qt::Horizontal);
model->index(0, 0);
model->itemData(QModelIndex());
QVariant cache;
model->match(QModelIndex(), -1, cache);
model->mimeTypes();
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
Q_ASSERT(model->rowCount() >= 0);
QVariant variant;
model->setData(QModelIndex(), variant, -1);
model->setHeaderData(-1, Qt::Horizontal, QVariant());
model->setHeaderData(0, Qt::Horizontal, QVariant());
model->setHeaderData(999999, Qt::Horizontal, QVariant());
QMap<int, QVariant> roles;
model->sibling(0, 0, QModelIndex());
model->span(QModelIndex());
model->supportedDropActions();
}
/*!
Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren()
Models that are dynamically populated are not as fully tested here.
*/
void ModelTest::rowCount()
{
// check top row
QModelIndex topIndex = model->index(0, 0, QModelIndex());
int rows = model->rowCount(topIndex);
Q_ASSERT(rows >= 0);
if (rows > 0)
Q_ASSERT(model->hasChildren(topIndex) == true);
QModelIndex secondLevelIndex = model->index(0, 0, topIndex);
if (secondLevelIndex.isValid()) { // not the top level
// check a row count where parent is valid
rows = model->rowCount(secondLevelIndex);
Q_ASSERT(rows >= 0);
if (rows > 0)
Q_ASSERT(model->hasChildren(secondLevelIndex) == true);
}
// The models rowCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren()
*/
void ModelTest::columnCount()
{
// check top row
QModelIndex topIndex = model->index(0, 0, QModelIndex());
Q_ASSERT(model->columnCount(topIndex) >= 0);
// check a column count where parent is valid
QModelIndex childIndex = model->index(0, 0, topIndex);
if (childIndex.isValid())
Q_ASSERT(model->columnCount(childIndex) >= 0);
// columnCount() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::hasIndex()
*/
void ModelTest::hasIndex()
{
// Make sure that invalid values returns an invalid index
Q_ASSERT(model->hasIndex(-2, -2) == false);
Q_ASSERT(model->hasIndex(-2, 0) == false);
Q_ASSERT(model->hasIndex(0, -2) == false);
int rows = model->rowCount();
int columns = model->columnCount();
Q_UNUSED(columns);
// check out of bounds
Q_ASSERT(model->hasIndex(rows, columns) == false);
Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false);
if (rows > 0)
Q_ASSERT(model->hasIndex(0, 0) == true);
// hasIndex() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::index()
*/
void ModelTest::index()
{
// Make sure that invalid values returns an invalid index
Q_ASSERT(model->index(-2, -2) == QModelIndex());
Q_ASSERT(model->index(-2, 0) == QModelIndex());
Q_ASSERT(model->index(0, -2) == QModelIndex());
int rows = model->rowCount();
int columns = model->columnCount();
Q_UNUSED(columns);
if (rows == 0)
return;
// Catch off by one errors
Q_ASSERT(model->index(rows, columns) == QModelIndex());
Q_ASSERT(model->index(0, 0).isValid() == true);
// Make sure that the same index is *always* returned
QModelIndex a = model->index(0, 0);
QModelIndex b = model->index(0, 0);
Q_ASSERT(a == b);
// index() is tested more extensively in checkChildren(),
// but this catches the big mistakes
}
/*!
Tests model's implementation of QAbstractItemModel::parent()
*/
void ModelTest::parent()
{
// Make sure the model wont crash and will return an invalid QModelIndex
// when asked for the parent of an invalid index.
Q_ASSERT(model->parent(QModelIndex()) == QModelIndex());
if (model->rowCount() == 0)
return;
// Column 0 | Column 1 |
// QModelIndex() | |
// \- topIndex | topIndex1 |
// \- childIndex | childIndex1 |
// Common error test #1, make sure that a top level index has a parent
// that is a invalid QModelIndex.
QModelIndex topIndex = model->index(0, 0, QModelIndex());
Q_ASSERT(model->parent(topIndex) == QModelIndex());
// Common error test #2, make sure that a second level index has a parent
// that is the first level index.
if (model->rowCount(topIndex) > 0) {
QModelIndex childIndex = model->index(0, 0, topIndex);
Q_ASSERT(model->parent(childIndex) == topIndex);
}
// Common error test #3, the second column should NOT have the same children
// as the first column in a row.
// Usually the second column shouldn't have children.
QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
if (model->rowCount(topIndex1) > 0) {
QModelIndex childIndex = model->index(0, 0, topIndex);
QModelIndex childIndex1 = model->index(0, 0, topIndex1);
Q_ASSERT(childIndex != childIndex1);
}
// Full test, walk n levels deep through the model making sure that all
// parent's children correctly specify their parent.
checkChildren(QModelIndex());
}
/*!
Called from the parent() test.
A model that returns an index of parent X should also return X when asking
for the parent of the index.
This recursive function does pretty extensive testing on the whole model in an
effort to catch edge cases.
This function assumes that rowCount(), columnCount() and index() already work.
If they have a bug it will point it out, but the above tests should have already
found the basic bugs because it is easier to figure out the problem in
those tests then this one.
*/
void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth)
{
// First just try walking back up the tree.
QModelIndex p = parent;
while (p.isValid())
p = p.parent();
// For models that are dynamically populated
if (model->canFetchMore(parent)) {
fetchingMore = true;
model->fetchMore(parent);
fetchingMore = false;
}
int rows = model->rowCount(parent);
int columns = model->columnCount(parent);
if (rows > 0)
Q_ASSERT(model->hasChildren(parent));
// Some further testing against rows(), columns(), and hasChildren()
Q_ASSERT(rows >= 0);
Q_ASSERT(columns >= 0);
if (rows > 0)
Q_ASSERT(model->hasChildren(parent) == true);
- //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows
+ //dbgKrita << "parent:" << model->data(parent).toString() << "rows:" << rows
// << "columns:" << columns << "parent column:" << parent.column();
Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false);
for (int r = 0; r < rows; ++r) {
if (model->canFetchMore(parent)) {
fetchingMore = true;
model->fetchMore(parent);
fetchingMore = false;
}
Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false);
for (int c = 0; c < columns; ++c) {
Q_ASSERT(model->hasIndex(r, c, parent) == true);
QModelIndex index = model->index(r, c, parent);
// rowCount() and columnCount() said that it existed...
Q_ASSERT(index.isValid() == true);
// index() should always return the same index when called twice in a row
QModelIndex modifiedIndex = model->index(r, c, parent);
Q_ASSERT(index == modifiedIndex);
// Make sure we get the same index if we request it twice in a row
QModelIndex a = model->index(r, c, parent);
QModelIndex b = model->index(r, c, parent);
Q_ASSERT(a == b);
// Some basic checking on the index that is returned
Q_ASSERT(index.model() == model);
Q_ASSERT(index.row() == r);
Q_ASSERT(index.column() == c);
// While you can technically return a QVariant usually this is a sign
// of an bug in data() Disable if this really is ok in your model.
Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true);
// If the next test fails here is some somewhat useful debug you play with.
/*
if (model->parent(index) != parent) {
- qDebug() << r << c << currentDepth << model->data(index).toString()
+ dbgKrita << r << c << currentDepth << model->data(index).toString()
<< model->data(parent).toString();
- qDebug() << index << parent << model->parent(index);
+ dbgKrita << index << parent << model->parent(index);
// And a view that you can even use to show the model.
//QTreeView view;
//view.setModel(model);
//view.show();
}*/
// Check that we can get back our real parent.
Q_ASSERT(model->parent(index) == parent);
// recursively go down the children
if (model->hasChildren(index) && currentDepth < 10) {
- //qDebug() << r << c << "has children" << model->rowCount(index);
+ //dbgKrita << r << c << "has children" << model->rowCount(index);
checkChildren(index, ++currentDepth);
- }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/
+ }/* else { if (currentDepth >= 10) dbgKrita << "checked 10 deep"; };*/
// make sure that after testing the children that the index doesn't change.
QModelIndex newerIndex = model->index(r, c, parent);
Q_ASSERT(index == newerIndex);
}
}
}
/*!
Tests model's implementation of QAbstractItemModel::data()
*/
void ModelTest::data()
{
// Invalid index should return an invalid qvariant
Q_ASSERT(!model->data(QModelIndex()).isValid());
if (model->rowCount() == 0)
return;
// A valid index should have a valid QVariant data
Q_ASSERT(model->index(0, 0).isValid());
// shouldn't be able to set data on an invalid index
Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false);
// General Purpose roles that should return a QString
QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
if (variant.isValid()) {
Q_ASSERT(qVariantCanConvert<QString>(variant));
}
variant = model->data(model->index(0, 0), Qt::StatusTipRole);
if (variant.isValid()) {
Q_ASSERT(qVariantCanConvert<QString>(variant));
}
variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
if (variant.isValid()) {
Q_ASSERT(qVariantCanConvert<QString>(variant));
}
// General Purpose roles that should return a QSize
variant = model->data(model->index(0, 0), Qt::SizeHintRole);
if (variant.isValid()) {
Q_ASSERT(qVariantCanConvert<QSize>(variant));
}
// General Purpose roles that should return a QFont
QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole);
if (fontVariant.isValid()) {
Q_ASSERT(qVariantCanConvert<QFont>(fontVariant));
}
// Check that the alignment is one we know about
QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
if (textAlignmentVariant.isValid()) {
int alignment = textAlignmentVariant.toInt();
Q_UNUSED(alignment);
Q_ASSERT(alignment == Qt::AlignLeft ||
alignment == Qt::AlignRight ||
alignment == Qt::AlignHCenter ||
alignment == Qt::AlignJustify ||
alignment == Qt::AlignTop ||
alignment == Qt::AlignBottom ||
alignment == Qt::AlignVCenter ||
alignment == Qt::AlignCenter ||
alignment == Qt::AlignAbsolute ||
alignment == Qt::AlignLeading ||
alignment == Qt::AlignTrailing);
}
// General Purpose roles that should return a QColor
QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole);
if (colorVariant.isValid()) {
Q_ASSERT(qVariantCanConvert<QColor>(colorVariant));
}
colorVariant = model->data(model->index(0, 0), Qt::TextColorRole);
if (colorVariant.isValid()) {
Q_ASSERT(qVariantCanConvert<QColor>(colorVariant));
}
// Check that the "check state" is one we know about.
QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
if (checkStateVariant.isValid()) {
int state = checkStateVariant.toInt();
Q_UNUSED(state);
Q_ASSERT(state == Qt::Unchecked ||
state == Qt::PartiallyChecked ||
state == Qt::Checked);
}
}
/*!
Store what is about to be inserted to make sure it actually happens
\sa rowsInserted()
*/
void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
Q_UNUSED(end);
Changing c;
c.parent = parent;
c.oldSize = model->rowCount(parent);
c.last = model->data(model->index(start - 1, 0, parent));
c.next = model->data(model->index(start, 0, parent));
insert.push(c);
}
/*!
Confirm that what was said was going to happen actually did
\sa rowsAboutToBeInserted()
*/
void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end)
{
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
Changing c = insert.pop();
Q_ASSERT(c.parent == parent);
Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent));
Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
/*
if (c.next != model->data(model->index(end + 1, 0, c.parent))) {
- qDebug() << start << end;
+ dbgKrita << start << end;
for (int i=0; i < model->rowCount(); ++i)
- qDebug() << model->index(i, 0).data().toString();
- qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent));
+ dbgKrita << model->index(i, 0).data().toString();
+ dbgKrita << c.next << model->data(model->index(end + 1, 0, c.parent));
}
*/
Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent)));
}
void ModelTest::layoutAboutToBeChanged()
{
for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i)
changing.append(QPersistentModelIndex(model->index(i, 0)));
}
void ModelTest::layoutChanged()
{
for (int i = 0; i < changing.count(); ++i) {
QPersistentModelIndex p = changing[i];
Q_ASSERT(p == model->index(p.row(), p.column(), p.parent()));
}
changing.clear();
}
/*!
Store what is about to be inserted to make sure it actually happens
\sa rowsRemoved()
*/
void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
Changing c;
c.parent = parent;
c.oldSize = model->rowCount(parent);
c.last = model->data(model->index(start - 1, 0, parent));
c.next = model->data(model->index(end + 1, 0, parent));
remove.push(c);
}
/*!
Confirm that what was said was going to happen actually did
\sa rowsAboutToBeRemoved()
*/
void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end)
{
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
Changing c = remove.pop();
Q_ASSERT(c.parent == parent);
Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent));
Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent)));
Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent)));
}
diff --git a/krita/ui/tests/scratchpad/scratchpad.cpp b/krita/ui/tests/scratchpad/scratchpad.cpp
index 40c9d248d1b..478c13acc7f 100644
--- a/krita/ui/tests/scratchpad/scratchpad.cpp
+++ b/krita/ui/tests/scratchpad/scratchpad.cpp
@@ -1,76 +1,76 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QFile>
#include <QString>
#include <QTextStream>
#include <QProcess>
#include <QTemporaryFile>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kapplication.h>
-#include <kdebug.h>
+#include <kis_debug.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <kis_scratch_pad.h>
int main(int argc, char** argv)
{
KAboutData aboutData("scratchpad",
0,
ki18n("scratchpad"),
"1.0",
ki18n("Test application for the single paint device scratchpad canvas"),
KAboutData::License_LGPL,
ki18n("(c) 2010 Boudewijn Rempt"),
KLocalizedString(),
"www.krita.org",
"submit@bugs.kde.org");
KCmdLineArgs::init(argc, argv, &aboutData);
KCmdLineOptions options;
options.add("+preset", ki18n("preset to load"));
KCmdLineArgs::addCmdLineOptions(options);
KApplication app;
KisScratchPad *scratchpad = new KisScratchPad();
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
if (args->count() > 0 ) {
QString fileName = args->arg(0);
if (QFile::exists(fileName)) {
KisPaintOpPresetSP preset = new KisPaintOpPreset(fileName);
preset->load();
if (preset->valid()) {
// scratchpad->setPreset(preset);
}
}
}
// const KoColorProfile* profile = KoColorSpaceRegistry::instance()->rgb8()->profile();
// scratchpad->setColorSpace(KoColorSpaceRegistry::instance()->rgb16());
// scratchpad->setDisplayProfile(profile);
// scratchpad->setCanvasColor(Qt::white);
scratchpad->show();
return app.exec();
}
diff --git a/krita/ui/tool/kis_tool.cc b/krita/ui/tool/kis_tool.cc
index 337b5e28473..b91f7111d86 100644
--- a/krita/ui/tool/kis_tool.cc
+++ b/krita/ui/tool/kis_tool.cc
@@ -1,811 +1,811 @@
/*
* Copyright (c) 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool.h"
#include "opengl/kis_opengl.h"
#include <QCursor>
#include <QLabel>
#include <QWidget>
#include <QPolygonF>
#include <QTransform>
#ifdef HAVE_OPENGL
#include <QOpenGLShaderProgram>
#include <QOpenGLContext>
#endif
#include <klocale.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <KoIcon.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include <KoToolBase.h>
#include <KoID.h>
#include <KoPointerEvent.h>
#include <KoViewConverter.h>
#include <KoSelection.h>
#include <KoAbstractGradient.h>
#include <KisViewManager.h>
#include <kis_selection.h>
#include <kis_image.h>
#include <kis_group_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_mask.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_paintop_preset.h>
#include <kis_paintop_settings.h>
#include <KoPattern.h>
#include <kis_transaction.h>
#include <kis_floating_message.h>
#include "opengl/kis_opengl_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include <recorder/kis_recorded_paint_action.h>
#include <kis_selection_mask.h>
#include "kis_resources_snapshot.h"
#include <KisView.h>
typedef void (*kis_glLogicOp)(int);
struct KisTool::Private {
Private()
: currentPattern(0),
currentGradient(0),
currentExposure(1.0),
currentGenerator(0),
optionWidget(0),
#ifdef HAVE_OPENGL
cursorShader(0),
#endif
useGLToolOutlineWorkaround(false)
{
}
QCursor cursor; // the cursor that should be shown on tool activation.
// From the canvas resources
KoPattern* currentPattern;
KoAbstractGradient* currentGradient;
KoColor currentFgColor;
KoColor currentBgColor;
float currentExposure;
KisFilterConfiguration* currentGenerator;
QWidget* optionWidget;
#ifdef HAVE_OPENGL
QOpenGLShaderProgram *cursorShader; // Make static instead of creating for all tools?
int cursorShaderModelViewProjectionUniform;
#endif
bool useGLToolOutlineWorkaround;
};
KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor)
: KoToolBase(canvas)
, d(new Private)
{
d->cursor = cursor;
m_isActive = false;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()));
connect(this, SIGNAL(isActiveChanged()), SLOT(resetCursorStyle()));
KActionCollection *collection = this->canvas()->canvasController()->actionCollection();
if (!collection->action("toggle_fg_bg")) {
KAction *toggleFgBg = new KAction(i18n("Swap Foreground and Background Color"), collection);
toggleFgBg->setShortcut(QKeySequence(Qt::Key_X));
collection->addAction("toggle_fg_bg", toggleFgBg);
}
if (!collection->action("reset_fg_bg")) {
KAction *resetFgBg = new KAction(i18n("Reset Foreground and Background Color"), collection);
resetFgBg->setShortcut(QKeySequence(Qt::Key_D));
collection->addAction("reset_fg_bg", resetFgBg);
}
addAction("toggle_fg_bg", dynamic_cast<KAction*>(collection->action("toggle_fg_bg")));
addAction("reset_fg_bg", dynamic_cast<KAction*>(collection->action("reset_fg_bg")));
setMode(HOVER_MODE);
QStringList qtVersion = QString(qVersion()).split('.');
int major = qtVersion.at(0).toInt();
int minor = qtVersion.at(1).toInt();
if (major == 4 && minor <= 6) {
d->useGLToolOutlineWorkaround = true;
}
else {
d->useGLToolOutlineWorkaround = false;
}
}
KisTool::~KisTool()
{
#ifdef HAVE_OPENGL
delete d->cursorShader;
#endif
delete d;
}
void KisTool::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
Q_UNUSED(toolActivation);
Q_UNUSED(shapes);
resetCursorStyle();
if (!canvas()) return;
if (!canvas()->resourceManager()) return;
d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value<KoColor>();
d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::BackgroundColor).value<KoColor>();
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) {
d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value<KoPattern*>();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) {
d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value<KoAbstractGradient*>();
}
KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (preset && preset->settings()) {
preset->settings()->activate();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) {
d->currentExposure = static_cast<float>(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble());
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) {
d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value<KisFilterConfiguration*>();
}
connect(actions().value("toggle_fg_bg"), SIGNAL(triggered()), SLOT(slotToggleFgBg()), Qt::UniqueConnection);
connect(actions().value("reset_fg_bg"), SIGNAL(triggered()), SLOT(slotResetFgBg()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigUndoDuringStrokeRequested()), SLOT(requestUndoDuringStroke()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigStrokeCancellationRequested()), SLOT(requestStrokeCancellation()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigStrokeEndRequested()), SLOT(requestStrokeEnd()), Qt::UniqueConnection);
m_isActive = true;
emit isActiveChanged();
}
void KisTool::deactivate()
{
bool result = true;
result &= disconnect(image().data(), SIGNAL(sigUndoDuringStrokeRequested()), this, 0);
result &= disconnect(image().data(), SIGNAL(sigStrokeCancellationRequested()), this, 0);
result &= disconnect(image().data(), SIGNAL(sigStrokeEndRequested()), this, 0);
result &= disconnect(actions().value("toggle_fg_bg"), 0, this, 0);
result &= disconnect(actions().value("reset_fg_bg"), 0, this, 0);
if (!result) {
- qWarning() << "WARNING: KisTool::deactivate() failed to disconnect"
+ warnKrita << "WARNING: KisTool::deactivate() failed to disconnect"
<< "some signal connections. Your actions might be executed twice!";
}
m_isActive = false;
emit isActiveChanged();
}
void KisTool::requestUndoDuringStroke()
{
/**
* Default implementation just cancells the stroke
*/
requestStrokeCancellation();
}
void KisTool::requestStrokeCancellation()
{
}
void KisTool::requestStrokeEnd()
{
}
void KisTool::canvasResourceChanged(int key, const QVariant & v)
{
switch (key) {
case(KoCanvasResourceManager::ForegroundColor):
d->currentFgColor = v.value<KoColor>();
break;
case(KoCanvasResourceManager::BackgroundColor):
d->currentBgColor = v.value<KoColor>();
break;
case(KisCanvasResourceProvider::CurrentPattern):
d->currentPattern = static_cast<KoPattern *>(v.value<void *>());
break;
case(KisCanvasResourceProvider::CurrentGradient):
d->currentGradient = static_cast<KoAbstractGradient *>(v.value<void *>());
break;
case(KisCanvasResourceProvider::HdrExposure):
d->currentExposure = static_cast<float>(v.toDouble());
break;
case(KisCanvasResourceProvider::CurrentGeneratorConfiguration):
d->currentGenerator = static_cast<KisFilterConfiguration*>(v.value<void *>());
break;
case(KisCanvasResourceProvider::CurrentPaintOpPreset):
emit statusTextChanged(v.value<KisPaintOpPresetSP>()->name());
break;
case(KisCanvasResourceProvider::CurrentKritaNode):
resetCursorStyle();
break;
default:
break; // Do nothing
};
}
void KisTool::updateSettingsViews()
{
}
QPointF KisTool::widgetCenterInWidgetPixels()
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
return converter->flakeToWidget(converter->flakeCenterPoint());
}
QPointF KisTool::convertDocumentToWidget(const QPointF& pt)
{
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->coordinatesConverter()->documentToWidget(pt);
}
QPointF KisTool::convertToPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point;
return image()->documentToPixel(e->point);
}
QPointF KisTool::convertToPixelCoord(const QPointF& pt)
{
if (!image())
return pt;
return image()->documentToPixel(pt);
}
QPoint KisTool::convertToIntPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point.toPoint();
return image()->documentToIntPixel(e->point);
}
QPointF KisTool::viewToPixel(const QPointF &viewCoord) const
{
if (!image())
return viewCoord;
return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord));
}
QRectF KisTool::convertToPt(const QRectF &rect)
{
if (!image())
return rect;
QRectF r;
//We add 1 in the following to the extreme coords because a pixel always has size
r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(),
int(1 + rect.right()) / image()->xRes(), int(1 + rect.bottom()) / image()->yRes());
return r;
}
QPointF KisTool::pixelToView(const QPoint &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QPointF KisTool::pixelToView(const QPointF &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QRectF KisTool::pixelToView(const QRectF &pixelRect) const
{
if (!image())
return pixelRect;
QPointF topLeft = pixelToView(pixelRect.topLeft());
QPointF bottomRight = pixelToView(pixelRect.bottomRight());
return QRectF(topLeft, bottomRight);
}
QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPolygon);
}
QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPath);
}
void KisTool::updateCanvasPixelRect(const QRectF &pixelRect)
{
canvas()->updateCanvas(convertToPt(pixelRect));
}
void KisTool::updateCanvasViewRect(const QRectF &viewRect)
{
canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect));
}
KisImageWSP KisTool::image() const
{
// For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
if (kisCanvas) {
return kisCanvas->currentImage();
}
return 0;
}
QCursor KisTool::cursor() const
{
return d->cursor;
}
void KisTool::notifyModified() const
{
if (image()) {
image()->setModified();
}
}
KoPattern * KisTool::currentPattern()
{
return d->currentPattern;
}
KoAbstractGradient * KisTool::currentGradient()
{
return d->currentGradient;
}
KisPaintOpPresetSP KisTool::currentPaintOpPreset()
{
return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
}
KisNodeSP KisTool::currentNode()
{
KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
return node;
}
KoColor KisTool::currentFgColor()
{
return d->currentFgColor;
}
KoColor KisTool::currentBgColor()
{
return d->currentBgColor;
}
KisImageWSP KisTool::currentImage()
{
return image();
}
KisFilterConfiguration * KisTool::currentGenerator()
{
return d->currentGenerator;
}
void KisTool::setMode(ToolMode mode) {
m_mode = mode;
}
KisTool::ToolMode KisTool::mode() const {
return m_mode;
}
KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) {
KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary);
return (AlternateAction)action;
}
void KisTool::activatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::deactivatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::beginPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event)
{
beginPrimaryAction(event);
}
void KisTool::continuePrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
bool KisTool::primaryActionSupportsHiResEvents() const
{
return false;
}
void KisTool::activateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::deactivateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action)
{
beginAlternateAction(event, action);
}
void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseTripleClickEvent(KoPointerEvent *event)
{
mouseDoubleClickEvent(event);
}
void KisTool::mousePressEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::deleteSelection()
{
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image(), currentNode(), 0, this->canvas()->resourceManager());
KisSelectionSP selection = resources->activeSelection();
KisNodeSP node = resources->currentNode();
if(node && node->hasEditablePaintDevice()) {
KisPaintDeviceSP device = node->paintDevice();
image()->barrierLock();
KisTransaction transaction(kundo2_i18n("Clear"), device);
QRect dirtyRect;
if (selection) {
dirtyRect = selection->selectedRect();
device->clearSelection(selection);
}
else {
dirtyRect = device->extent();
device->clear();
}
transaction.commit(image()->undoAdapter());
device->setDirty(dirtyRect);
image()->unlock();
}
else {
KoToolBase::deleteSelection();
}
}
void KisTool::setupPaintAction(KisRecordedPaintAction* action)
{
action->setPaintColor(currentFgColor());
action->setBackgroundColor(currentBgColor());
}
QWidget* KisTool::createOptionWidget()
{
d->optionWidget = new QLabel(i18n("No options"));
d->optionWidget->setObjectName("SpecialSpacer");
return d->optionWidget;
}
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#define PROGRAM_VERTEX_ATTRIBUTE 0
void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path)
{
#ifdef HAVE_OPENGL
KisOpenGLCanvas2 *canvasWidget = dynamic_cast<KisOpenGLCanvas2 *>(canvas()->canvasWidget());
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (canvasWidget && !d->useGLToolOutlineWorkaround && ctx) {
painter->beginNativePainting();
if (d->cursorShader == 0) {
d->cursorShader = new QOpenGLShaderProgram();
d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.vert"));
d->cursorShader->addShaderFromSourceFile(QOpenGLShader::Fragment, KGlobal::dirs()->findResource("data", "krita/shaders/cursor.frag"));
d->cursorShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
if (! d->cursorShader->link()) {
- qDebug() << "OpenGL error" << canvasWidget->glGetError();
+ dbgKrita << "OpenGL error" << canvasWidget->glGetError();
qFatal("Failed linking cursor shader");
}
Q_ASSERT(d->cursorShader->isLinked());
d->cursorShaderModelViewProjectionUniform = d->cursorShader->uniformLocation("modelViewProjection");
}
d->cursorShader->bind();
// setup the mvp transformation
KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kritaCanvas);
const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, canvasWidget->width(), canvasWidget->height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(converter->flakeToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->cursorShader->setUniformValue(d->cursorShaderModelViewProjectionUniform, modelMatrix);
canvasWidget->glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// XXX: glLogicOp not in ES 2.0 -- in that case, it would be better to use another method.
// It is defined in 3.1 core profile onward.
canvasWidget->glEnable(GL_COLOR_LOGIC_OP);
kis_glLogicOp ptr_glLogicOp = (kis_glLogicOp)ctx->getProcAddress("glLogicOp"); //Doing this lookup in a loop is a bit expensive.
if (ptr_glLogicOp) {
ptr_glLogicOp(GL_XOR);
}
// setup the array of vertices
QVector<QVector3D> vertices;
QList<QPolygonF> subPathPolygons = path.toSubpathPolygons();
for (int i=0; i<subPathPolygons.size(); i++) {
const QPolygonF& polygon = subPathPolygons.at(i);
for (int j=0; j < polygon.count(); j++) {
QPointF p = polygon.at(j);
vertices << QVector3D(p.x(), p.y(), 0.f);
}
d->cursorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->cursorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData());
canvasWidget->glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
vertices.clear();
}
canvasWidget->glDisable(GL_COLOR_LOGIC_OP);
d->cursorShader->release();
painter->endNativePainting();
}
else if (canvasWidget && d->useGLToolOutlineWorkaround) {
#else
if (d->useGLToolOutlineWorkaround) {
#endif // HAVE_OPENGL
// the workaround option is enabled for Qt 4.6 < 4.6.3... Only relevant on CentOS.
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
QPen pen = painter->pen();
pen.setStyle(Qt::SolidLine);
pen.setWidth(1);
pen.setColor(QColor(255, 255, 255));
painter->setPen(pen);
painter->drawPath(path);
pen.setStyle(Qt::DotLine);
pen.setWidth(1);
pen.setColor(QColor(0, 0, 0));
painter->setPen(pen);
painter->drawPath(path);
}
else {
painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter->setPen(QColor(128, 255, 128));
painter->drawPath(path);
}
}
void KisTool::resetCursorStyle()
{
useCursor(d->cursor);
}
bool KisTool::overrideCursorIfNotEditable()
{
// override cursor for canvas iff this tool is active
// and we can't paint on the active layer
if (isActive()) {
KisNodeSP node = currentNode();
if (node && !node->isEditable()) {
canvas()->setCursor(Qt::ForbiddenCursor);
return true;
}
}
return false;
}
bool KisTool::isActive() const
{
return m_isActive;
}
void KisTool::slotToggleFgBg()
{
KoCanvasResourceManager* resourceManager = canvas()->resourceManager();
KoColor newFg = resourceManager->backgroundColor();
KoColor newBg = resourceManager->foregroundColor();
/**
* NOTE: Some of color selectors do not differentiate foreground
* and background colors, so if one wants them to end up
* being set up to foreground color, it should be set the
* last.
*/
resourceManager->setBackgroundColor(newBg);
resourceManager->setForegroundColor(newFg);
}
void KisTool::slotResetFgBg()
{
KoCanvasResourceManager* resourceManager = canvas()->resourceManager();
// see a comment in slotToggleFgBg()
resourceManager->setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
resourceManager->setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
}
void KisTool::setCurrentNodeLocked(bool locked)
{
if (currentNode()) {
currentNode()->setSystemLocked(locked, false);
}
}
bool KisTool::nodeEditable()
{
KisNodeSP node = currentNode();
if (!node) {
return false;
}
if (!node->isEditable()) {
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
QString message;
if (!node->visible() && node->userLocked()) {
message = i18n("Layer is locked and invisible.");
} else if (node->userLocked()) {
message = i18n("Layer is locked.");
} else if(!node->visible()) {
message = i18n("Layer is invisible.");
} else {
message = i18n("Group not editable.");
}
kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked"));
}
return node->isEditable();
}
bool KisTool::selectionEditable()
{
KisCanvas2 * kisCanvas = static_cast<KisCanvas2*>(canvas());
KisViewManager * view = kisCanvas->viewManager();
bool editable = view->selectionEditable();
if (!editable) {
KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), koIcon("object-locked"));
}
return editable;
}
void KisTool::listenToModifiers(bool listen)
{
Q_UNUSED(listen);
}
bool KisTool::listeningToModifiers()
{
return false;
}
diff --git a/krita/ui/tool/kis_tool.h b/krita/ui/tool/kis_tool.h
index 00f2f3c24a3..0393d1a5582 100644
--- a/krita/ui/tool/kis_tool.h
+++ b/krita/ui/tool/kis_tool.h
@@ -1,368 +1,368 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_H_
#define KIS_TOOL_H_
#include <QCursor>
#include <KoColor.h>
#include <KoToolBase.h>
#include <KoID.h>
#include <KoCanvasResourceManager.h>
#include <kritaui_export.h>
#include <kis_types.h>
#define PRESS_CONDITION(_event, _mode, _button, _modifier) \
(this->mode() == (_mode) && (_event)->button() == (_button) && \
(_event)->modifiers() == (_modifier))
#define PRESS_CONDITION_WB(_event, _mode, _button, _modifier) \
(this->mode() == (_mode) && (_event)->button() & (_button) && \
(_event)->modifiers() == (_modifier))
#define PRESS_CONDITION_OM(_event, _mode, _button, _modifier) \
(this->mode() == (_mode) && (_event)->button() == (_button) && \
((_event)->modifiers() & (_modifier) || \
(_event)->modifiers() == Qt::NoModifier))
#define RELEASE_CONDITION(_event, _mode, _button) \
(this->mode() == (_mode) && (_event)->button() == (_button))
#define RELEASE_CONDITION_WB(_event, _mode, _button) \
(this->mode() == (_mode) && (_event)->button() & (_button))
#define MOVE_CONDITION(_event, _mode) (this->mode() == (_mode))
#ifdef __GNUC__
-#define WARN_WRONG_MODE(_mode) qWarning() << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!"
+#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!"
#else
-#define WARN_WRONG_MODE(_mode) qWarning() << "Unexpected tool event has come while being mode" << _mode << "!"
+#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!"
#endif
#define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; }
class KoCanvasBase;
class KoPattern;
class KoAbstractGradient;
class KisFilterConfiguration;
class QPainter;
class QPainterPath;
class QPolygonF;
class KisRecordedPaintAction;
/// Definitions of the toolgroups of Krita
static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines
static const QString TOOL_TYPE_FREEHAND = "1 Krita/Freehand"; // Freehand drawing tools
static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer;
static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas
static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc.
static const QString TOOL_TYPE_SELECTED = "5 Krita/Select"; // Tools that select pixels
//activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself
static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always";
class KRITAUI_EXPORT KisTool
: public KoToolBase
{
Q_OBJECT
Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged)
public:
enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02 };
KisTool(KoCanvasBase * canvas, const QCursor & cursor);
virtual ~KisTool();
virtual int flags() const { return 0; }
void deleteSelection();
// KoToolBase Implementation.
public:
/**
* Called by KisToolProxy when the primary action of the tool is
* going to be started now, that is when all the modifiers are
* pressed and the only thing left is just to press the mouse
* button. On coming of this callback the tool is supposed to
* prepare the cursor and/or the outline to show the user shat is
* going to happen next
*/
virtual void activatePrimaryAction();
/**
* Called by KisToolProxy when the primary is no longer possible
* to be started now, e.g. when its modifiers and released. The
* tool is supposed revert all the preparetions it has doen in
* activatePrimaryAction().
*/
virtual void deactivatePrimaryAction();
/**
* Called by KisToolProxy when a primary action for the tool is
* started. The \p event stores the original event that
* started the stroke. The \p event is _accepted_ by default. If
* the tool decides to ignore this particular action (e.g. when
* the node is not editable), it should call event->ignore(). Then
* no further continuePrimaryAction() or endPrimaryAction() will
* be called until the next user action.
*/
virtual void beginPrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is in progress
* of pointer movement. If the tool has ignored the event in
* beginPrimaryAction(), this method will not be called.
*/
virtual void continuePrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is being
* finished, that is while mouseRelease or tabletRelease event.
* If the tool has ignored the event in beginPrimaryAction(), this
* method will not be called.
*/
virtual void endPrimaryAction(KoPointerEvent *event);
/**
* The same as beginPrimaryAction(), but called when the stroke is
* started by a double-click
*
* \see beginPrimaryAction()
*/
virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event);
/**
* Returns true if the tool can handle (and wants to handle) a
* very tight flow of input events from the tablet
*/
virtual bool primaryActionSupportsHiResEvents() const;
enum ToolAction {
Primary,
AlternateChangeSize,
AlternatePickFgNode,
AlternatePickBgNode,
AlternatePickFgImage,
AlternatePickBgImage,
AlternateSecondary,
AlternateThird,
AlternateFourth,
AlternateFifth,
Alternate_NONE = 10000
};
enum AlternateAction {
ChangeSize = AlternateChangeSize,
PickFgNode = AlternatePickFgNode,
PickBgNode = AlternatePickBgNode,
PickFgImage = AlternatePickFgImage,
PickBgImage = AlternatePickBgImage,
Secondary = AlternateSecondary,
Third = AlternateThird,
Fourth = AlternateFourth,
Fifth = AlternateFifth,
NONE = 10000
};
static AlternateAction actionToAlternateAction(ToolAction action);
virtual void activateAlternateAction(AlternateAction action);
virtual void deactivateAlternateAction(AlternateAction action);
virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action);
void mousePressEvent(KoPointerEvent *event);
void mouseDoubleClickEvent(KoPointerEvent *event);
void mouseTripleClickEvent(KoPointerEvent *event);
void mouseReleaseEvent(KoPointerEvent *event);
void mouseMoveEvent(KoPointerEvent *event);
bool isActive() const;
public Q_SLOTS:
virtual void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes);
virtual void deactivate();
virtual void canvasResourceChanged(int key, const QVariant & res);
// Implement this slot in case there are any widgets or properties which need
// to be updated after certain operations, to reflect the inner state correctly.
// At the moment this is used for smoothing options in the freehand brush, but
// this will likely be expanded.
virtual void updateSettingsViews();
Q_SIGNALS:
void isActiveChanged();
protected:
// conversion methods are also needed by the paint information builder
friend class KisToolPaintingInformationBuilder;
/// Convert from native (postscript points) to image pixel
/// coordinates.
QPointF convertToPixelCoord(KoPointerEvent *e);
QPointF convertToPixelCoord(const QPointF& pt);
protected:
QPointF widgetCenterInWidgetPixels();
QPointF convertDocumentToWidget(const QPointF& pt);
/// Convert from native (postscript points) to integer image pixel
/// coordinates. This truncates the floating point components and
/// should be used in preference to QPointF::toPoint(), which rounds,
/// to ensure the cursor acts on the pixel it is visually over.
QPoint convertToIntPixelCoord(KoPointerEvent *e);
QRectF convertToPt(const QRectF &rect);
QPointF viewToPixel(const QPointF &viewCoord) const;
/// Convert an integer pixel coordinate into a view coordinate.
/// The view coordinate is at the centre of the pixel.
QPointF pixelToView(const QPoint &pixelCoord) const;
/// Convert a floating point pixel coordinate into a view coordinate.
QPointF pixelToView(const QPointF &pixelCoord) const;
/// Convert a pixel rectangle into a view rectangle.
QRectF pixelToView(const QRectF &pixelRect) const;
/// Convert a pixel path into a view path
QPainterPath pixelToView(const QPainterPath &pixelPath) const;
/// Convert a pixel polygon into a view path
QPolygonF pixelToView(const QPolygonF &pixelPolygon) const;
/// Update the canvas for the given rectangle in image pixel coordinates.
void updateCanvasPixelRect(const QRectF &pixelRect);
/// Update the canvas for the given rectangle in view coordinates.
void updateCanvasViewRect(const QRectF &viewRect);
virtual QWidget* createOptionWidget();
/**
* To determine whether this tool will change its behavior when
* modifier keys are pressed
*/
virtual bool listeningToModifiers();
/**
* Request that this tool no longer listen to modifier keys
* (Responding to the request is optional)
*/
virtual void listenToModifiers(bool listen);
protected:
KisImageWSP image() const;
QCursor cursor() const;
/// Call this to set the document modified
void notifyModified() const;
KisImageWSP currentImage();
KoPattern* currentPattern();
KoAbstractGradient *currentGradient();
KisNodeSP currentNode();
KoColor currentFgColor();
KoColor currentBgColor();
KisPaintOpPresetSP currentPaintOpPreset();
KisFilterConfiguration *currentGenerator();
virtual void setupPaintAction(KisRecordedPaintAction* action);
/// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible
/// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates.
void paintToolOutline(QPainter * painter, const QPainterPath &path);
/// Sets the systemLocked for the current node, this will not deactivate the tool buttons
void setCurrentNodeLocked(bool locked);
/// Checks checks if the current node is editable
bool nodeEditable();
/// Checks checks if the selection is editable, only applies to local selection as global selection is always editable
bool selectionEditable();
/// Override the cursor appropriately if current node is not editable
bool overrideCursorIfNotEditable();
protected:
enum ToolMode {
HOVER_MODE,
PAINT_MODE,
SECONDARY_PAINT_MODE,
MIRROR_AXIS_SETUP_MODE,
GESTURE_MODE,
PAN_MODE,
OTHER // not used now
};
virtual void setMode(ToolMode mode);
virtual ToolMode mode() const;
protected Q_SLOTS:
/**
* Called whenever the configuration settings change.
*/
virtual void resetCursorStyle();
/**
* Called when the user requested undo while the stroke is
* active. If you tool supports undo of the part of its actions,
* override this method and do the needed work there.
*
* NOTE: Default implementation forwards this request to
* requestStrokeCancellation() method, so that the stroke
* would be cancelled.
*/
virtual void requestUndoDuringStroke();
/**
* Called when the user requested the cancellation of the current
* stroke. If you tool supports cancelling, override this method
* and do the needed work there
*/
virtual void requestStrokeCancellation();
/**
* Called when the image decided that the stroke should better be
* ended. If you tool supports long strokes, override this method
* and do the needed work there
*/
virtual void requestStrokeEnd();
private Q_SLOTS:
void slotToggleFgBg();
void slotResetFgBg();
private:
ToolMode m_mode;
bool m_isActive;
struct Private;
Private* const d;
};
#endif // KIS_TOOL_H_
diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis_tool_freehand_helper.cpp
index ac65d704470..d41f0c5fe98 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -1,845 +1,845 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_freehand_helper.h"
#include <QTimer>
#include <QQueue>
#include <klocale.h>
#include <KoPointerEvent.h>
#include <KoCanvasResourceManager.h>
#include "kis_distance_information.h"
#include "kis_painting_information_builder.h"
#include "kis_recording_adapter.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_smoothing_options.h"
#include "kis_paintop_preset.h"
#include "kis_paintop_utils.h"
#include "kis_update_time_monitor.h"
#include <math.h>
//#define DEBUG_BEZIER_CURVES
struct KisToolFreehandHelper::Private
{
KisPaintingInformationBuilder *infoBuilder;
KisRecordingAdapter *recordingAdapter;
KisStrokesFacade *strokesFacade;
KUndo2MagicString transactionText;
bool haveTangent;
QPointF previousTangent;
bool hasPaintAtLeastOnce;
QTime strokeTime;
QTimer strokeTimeoutTimer;
QVector<PainterInfo*> painterInfos;
KisResourcesSnapshotSP resources;
KisStrokeId strokeId;
KisPaintInformation previousPaintInformation;
KisPaintInformation olderPaintInformation;
KisSmoothingOptionsSP smoothingOptions;
QTimer airbrushingTimer;
QList<KisPaintInformation> history;
QList<qreal> distanceHistory;
KisPaintOpUtils::PositionHistory lastOutlinePos;
// Stabilizer data
QQueue<KisPaintInformation> stabilizerDeque;
KisPaintInformation stabilizerLastPaintInfo;
QTimer stabilizerPollTimer;
int canvasRotation;
bool canvasMirroredH;
KisPaintInformation
getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
const KisPaintInformation &lastPaintInfo);
qreal effectiveSmoothnessDistance() const;
};
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText,
KisRecordingAdapter *recordingAdapter)
: m_d(new Private())
{
m_d->infoBuilder = infoBuilder;
m_d->recordingAdapter = recordingAdapter;
m_d->transactionText = transactionText;
m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions());
m_d->canvasRotation = 0;
m_d->strokeTimeoutTimer.setSingleShot(true);
connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
}
KisToolFreehandHelper::~KisToolFreehandHelper()
{
delete m_d;
}
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
{
m_d->smoothingOptions = smoothingOptions;
}
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
return m_d->smoothingOptions;
}
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettings *globalSettings,
KisPaintOpSettings::OutlineMode mode) const
{
const KisPaintOpSettings *settings = globalSettings;
KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
info.setCanvasRotation(m_d->canvasRotation);
info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
if (!m_d->painterInfos.isEmpty()) {
settings = m_d->resources->currentPaintOpPreset()->settings();
info = m_d->previousPaintInformation;
distanceInfo = *m_d->painterInfos.first()->dragDistance;
}
KisPaintInformation::DistanceInformationRegistrar registrar =
info.registerDistanceInformation(&distanceInfo);
QPainterPath outline = settings->brushOutline(info, mode);
if (m_d->resources &&
m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER &&
m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
outline.addEllipse(info.pos(), R, R);
}
return outline;
}
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
KoCanvasResourceManager *resourceManager,
KisImageWSP image, KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
KisPaintInformation pi =
m_d->infoBuilder->startStroke(event, elapsedStrokeTime());
initPaintImpl(pi,
resourceManager,
image,
currentNode,
strokesFacade,
undoAdapter,
overrideNode,
bounds);
}
bool KisToolFreehandHelper::isRunning() const
{
return m_d->strokeId;
}
void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation,
KoCanvasResourceManager *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
Q_UNUSED(overrideNode);
m_d->strokesFacade = strokesFacade;
m_d->haveTangent = false;
m_d->previousTangent = QPointF();
m_d->hasPaintAtLeastOnce = false;
m_d->strokeTime.start();
m_d->previousPaintInformation = previousPaintInformation;
createPainters(m_d->painterInfos,
m_d->previousPaintInformation.pos(),
m_d->previousPaintInformation.currentTime());
m_d->resources = new KisResourcesSnapshot(image,
currentNode,
undoAdapter,
resourceManager,
bounds);
if(overrideNode) {
m_d->resources->setCurrentNode(overrideNode);
}
if(m_d->recordingAdapter) {
m_d->recordingAdapter->startStroke(image, m_d->resources);
}
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(),
m_d->resources->indirectPaintingCompositeOp(),
m_d->resources, m_d->painterInfos, m_d->transactionText);
m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
m_d->history.clear();
m_d->distanceHistory.clear();
if(m_d->resources->needsAirbrushing()) {
m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
m_d->airbrushingTimer.start();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerStart(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2)
{
if (tangent1.isNull() || tangent2.isNull()) return;
const qreal maxSanePoint = 1e6;
QPointF controlTarget1;
QPointF controlTarget2;
// Shows the direction in which control points go
QPointF controlDirection1 = pi1.pos() + tangent1;
QPointF controlDirection2 = pi2.pos() - tangent2;
// Lines in the direction of the control points
QLineF line1(pi1.pos(), controlDirection1);
QLineF line2(pi2.pos(), controlDirection2);
// Lines to check whether the control points lay on the opposite
// side of the line
QLineF line3(controlDirection1, controlDirection2);
QLineF line4(pi1.pos(), pi2.pos());
QPointF intersection;
if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
qreal controlLength = line4.length() / 2;
line1.setLength(controlLength);
line2.setLength(controlLength);
controlTarget1 = line1.p2();
controlTarget2 = line2.p2();
} else {
QLineF::IntersectType type = line1.intersect(line2, &intersection);
if (type == QLineF::NoIntersection ||
intersection.manhattanLength() > maxSanePoint) {
intersection = 0.5 * (pi1.pos() + pi2.pos());
-// qDebug() << "WARINING: there is no intersection point "
+// dbgKrita << "WARINING: there is no intersection point "
// << "in the basic smoothing algoriths";
}
controlTarget1 = intersection;
controlTarget2 = intersection;
}
// shows how near to the controlTarget the value raises
qreal coeff = 0.8;
qreal velocity1 = QLineF(QPointF(), tangent1).length();
qreal velocity2 = QLineF(QPointF(), tangent2).length();
if (velocity1 == 0.0 || velocity2 == 0.0) {
velocity1 = 1e-6;
velocity2 = 1e-6;
- qWarning() << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
+ warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
}
qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
// the controls should not differ more than 50%
similarity = qMax(similarity, qreal(0.5));
// when the controls are symmetric, their size should be smaller
// to avoid corner-like curves
coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Q_ASSERT(coeff > 0);
QPointF control1;
QPointF control2;
if (velocity1 > velocity2) {
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
coeff *= similarity;
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
} else {
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
coeff *= similarity;
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
}
paintBezierCurve(pi1,
control1,
control2,
pi2);
}
qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const
{
const qreal effectiveSmoothnessDistance =
!smoothingOptions->useScalableDistance() ?
smoothingOptions->smoothnessDistance() :
smoothingOptions->smoothnessDistance() / resources->effectiveZoom();
return effectiveSmoothnessDistance;
}
void KisToolFreehandHelper::paint(KoPointerEvent *event)
{
KisPaintInformation info =
m_d->infoBuilder->continueStroke(event,
elapsedStrokeTime());
info.setCanvasRotation( m_d->canvasRotation );
info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos());
/**
* Smooth the coordinates out using the history and the
* distance. This is a heavily modified version of an algo used in
* Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and
* http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main
* differences are:
*
* 1) It uses 'distance' instead of 'velocity', since time
* measurements are too unstable in realworld environment
*
* 2) There is no 'Quality' parameter, since the number of samples
* is calculated automatically
*
* 3) 'Tail Aggressiveness' is used for controling the end of the
* stroke
*
* 4) The formila is a little bit different: 'Distance' parameter
* stands for $3 \Sigma$
*/
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
&& m_d->smoothingOptions->smoothnessDistance() > 0.0) {
{ // initialize current distance
QPointF prevPos;
if (!m_d->history.isEmpty()) {
const KisPaintInformation &prevPi = m_d->history.last();
prevPos = prevPi.pos();
} else {
prevPos = m_d->previousPaintInformation.pos();
}
qreal currentDistance = QVector2D(info.pos() - prevPos).length();
m_d->distanceHistory.append(currentDistance);
}
m_d->history.append(info);
qreal x = 0.0;
qreal y = 0.0;
if (m_d->history.size() > 3) {
const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range
qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
qreal gaussianWeight2 = sigma * sigma;
qreal distanceSum = 0.0;
qreal scaleSum = 0.0;
qreal pressure = 0.0;
qreal baseRate = 0.0;
Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
for (int i = m_d->history.size() - 1; i >= 0; i--) {
qreal rate = 0.0;
const KisPaintInformation nextInfo = m_d->history.at(i);
double distance = m_d->distanceHistory.at(i);
Q_ASSERT(distance >= 0.0);
qreal pressureGrad = 0.0;
if (i < m_d->history.size() - 1) {
pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
if (pressureGrad > 0.0 ) {
pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
}
}
if (gaussianWeight2 != 0.0) {
distanceSum += distance;
rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
}
if (m_d->history.size() - i == 1) {
baseRate = rate;
} else if (baseRate / rate > 100) {
break;
}
scaleSum += rate;
x += rate * nextInfo.pos().x();
y += rate * nextInfo.pos().y();
if (m_d->smoothingOptions->smoothPressure()) {
pressure += rate * nextInfo.pressure();
}
}
if (scaleSum != 0.0) {
x /= scaleSum;
y /= scaleSum;
if (m_d->smoothingOptions->smoothPressure()) {
pressure /= scaleSum;
}
}
if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
info.setPos(QPointF(x, y));
if (m_d->smoothingOptions->smoothPressure()) {
info.setPressure(pressure);
}
m_d->history.last() = info;
}
}
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
|| m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
{
// Now paint between the coordinates, using the bezier curve interpolation
if (!m_d->haveTangent) {
m_d->haveTangent = true;
m_d->previousTangent =
(info.pos() - m_d->previousPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
} else {
QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
m_d->previousTangent, newTangent);
m_d->previousTangent = newTangent;
}
m_d->olderPaintInformation = m_d->previousPaintInformation;
m_d->strokeTimeoutTimer.start(100);
}
else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
paintLine(m_d->previousPaintInformation, info);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
m_d->stabilizerLastPaintInfo = info;
} else {
m_d->previousPaintInformation = info;
}
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.start();
}
}
void KisToolFreehandHelper::endPaint()
{
if (!m_d->hasPaintAtLeastOnce) {
paintAt(m_d->previousPaintInformation);
} else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
finishStroke();
}
m_d->strokeTimeoutTimer.stop();
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerEnd();
}
/**
* There might be some timer events still pending, so
* we should cancel them. Use this flag for the purpose.
* Please note that we are not in MT here, so no mutex
* is needed
*/
m_d->painterInfos.clear();
m_d->strokesFacade->endStroke(m_d->strokeId);
m_d->strokeId.clear();
if(m_d->recordingAdapter) {
m_d->recordingAdapter->endStroke();
}
}
void KisToolFreehandHelper::cancelPaint()
{
if (!m_d->strokeId) return;
m_d->strokeTimeoutTimer.stop();
if (m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->stabilizerPollTimer.isActive()) {
m_d->stabilizerPollTimer.stop();
}
// see a comment in endPaint()
m_d->painterInfos.clear();
m_d->strokesFacade->cancelStroke(m_d->strokeId);
m_d->strokeId.clear();
if(m_d->recordingAdapter) {
//FIXME: not implemented
//m_d->recordingAdapter->cancelStroke();
}
}
int KisToolFreehandHelper::elapsedStrokeTime() const
{
return m_d->strokeTime.elapsed();
}
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = qRound(m_d->effectiveSmoothnessDistance());
sampleSize = qMax(3, sampleSize);
// Fill the deque with the current value repeated until filling the sample
m_d->stabilizerDeque.clear();
for (int i = sampleSize; i > 0; i--) {
m_d->stabilizerDeque.enqueue(firstPaintInfo);
}
m_d->stabilizerLastPaintInfo = firstPaintInfo;
// Poll and draw each millisecond
m_d->stabilizerPollTimer.setInterval(1);
m_d->stabilizerPollTimer.start();
}
KisPaintInformation
KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
const KisPaintInformation &lastPaintInfo)
{
KisPaintInformation result(lastPaintInfo);
if (queue.size() > 1) {
QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
/**
* The first point is going to be overridden by lastPaintInfo, skip it.
*/
it++;
int i = 2;
if (smoothingOptions->stabilizeSensors()) {
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result = KisPaintInformation::mix(k, *it, result);
it++;
i++;
}
} else{
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result = KisPaintInformation::mixOnlyPosition(k, *it, result);
it++;
i++;
}
}
}
return result;
}
void KisToolFreehandHelper::stabilizerPollAndPaint()
{
KisPaintInformation newInfo =
m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, m_d->stabilizerLastPaintInfo);
bool canPaint = true;
if (m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
QPointF diff = m_d->stabilizerLastPaintInfo.pos() - m_d->previousPaintInformation.pos();
qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
canPaint = dx > R;
}
if (canPaint) {
paintLine(m_d->previousPaintInformation, newInfo);
m_d->previousPaintInformation = newInfo;
// Push the new entry through the queue
m_d->stabilizerDeque.dequeue();
m_d->stabilizerDeque.enqueue(m_d->stabilizerLastPaintInfo);
emit requestExplicitUpdateOutline();
} else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
while (it != end) {
*it = m_d->previousPaintInformation;
++it;
}
}
}
void KisToolFreehandHelper::stabilizerEnd()
{
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = m_d->smoothingOptions->smoothnessDistance();
assert(sampleSize > 0);
// Stop the timer
m_d->stabilizerPollTimer.stop();
// Finish the line
for (int i = sampleSize; i > 0; i--) {
// In each iteration we add the latest paint info and delete the oldest
// After `sampleSize` iterations the deque will be filled with the latest
// value and we will have reached the end point.
if (m_d->smoothingOptions->finishStabilizedCurve()) {
stabilizerPollAndPaint();
}
}
}
const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const
{
return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0;
}
void KisToolFreehandHelper::finishStroke()
{
if (m_d->haveTangent) {
m_d->haveTangent = false;
QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
(m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation,
m_d->previousPaintInformation,
m_d->previousTangent,
newTangent);
}
}
void KisToolFreehandHelper::doAirbrushing()
{
if(!m_d->painterInfos.isEmpty()) {
paintAt(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::paintAt(PainterInfo *painterInfo,
const KisPaintInformation &pi)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfo, pi));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addPoint(pi);
}
}
void KisToolFreehandHelper::paintLine(PainterInfo *painterInfo,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfo, pi1, pi2));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addLine(pi1, pi2);
}
}
void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
#ifdef DEBUG_BEZIER_CURVES
KisPaintInformation tpi1;
KisPaintInformation tpi2;
tpi1 = pi1;
tpi2 = pi2;
tpi1.setPressure(0.3);
tpi2.setPressure(0.3);
paintLine(tpi1, tpi2);
tpi1.setPressure(0.6);
tpi2.setPressure(0.3);
tpi1.setPos(pi1.pos());
tpi2.setPos(control1);
paintLine(tpi1, tpi2);
tpi1.setPos(pi2.pos());
tpi2.setPos(control2);
paintLine(tpi1, tpi2);
#endif
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfo,
pi1, control1, control2, pi2));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2);
}
}
void KisToolFreehandHelper::createPainters(QVector<PainterInfo*> &painterInfos,
const QPointF &lastPosition,
int lastTime)
{
painterInfos <<
new PainterInfo(new KisPainter(),
new KisDistanceInformation(lastPosition, lastTime));
}
void KisToolFreehandHelper::paintAt(const QVector<PainterInfo*> &painterInfos,
const KisPaintInformation &pi)
{
paintAt(painterInfos.first(), pi);
}
void KisToolFreehandHelper::paintLine(const QVector<PainterInfo*> &painterInfos,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(painterInfos.first(), pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const QVector<PainterInfo*> &painterInfos,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(painterInfos.first(), pi1, control1, control2, pi2);
}
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
paintAt(m_d->painterInfos, pi);
}
void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(m_d->painterInfos, pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(m_d->painterInfos, pi1, control1, control2, pi2);
}
int KisToolFreehandHelper::canvasRotation()
{
return m_d->canvasRotation;
}
void KisToolFreehandHelper::setCanvasRotation(int rotation)
{
m_d->canvasRotation = rotation;
}
bool KisToolFreehandHelper::canvasMirroredH()
{
return m_d->canvasMirroredH;
}
void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored)
{
m_d->canvasMirroredH = mirrored;
}
diff --git a/krita/ui/tool/kis_tool_paint.cc b/krita/ui/tool/kis_tool_paint.cc
index 380e73007ec..be6b5cb6cd8 100644
--- a/krita/ui/tool/kis_tool_paint.cc
+++ b/krita/ui/tool/kis_tool_paint.cc
@@ -1,748 +1,748 @@
/*
* Copyright (c) 2003-2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_paint.h"
#include <algorithm>
#include <QWidget>
#include <QRect>
#include <QLayout>
#include <QLabel>
#include <QPushButton>
#include <QWhatsThis>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QKeyEvent>
#include <QEvent>
#include <QVariant>
#include <QAction>
-#include <QDebug>
+#include <kis_debug.h>
#include <QPoint>
#include <kis_debug.h>
#include <klocale.h>
#include <kactioncollection.h>
#include <kaction.h>
#include <KoIcon.h>
#include <KoShape.h>
#include <KoCanvasResourceManager.h>
#include <KoColorSpace.h>
#include <KoPointerEvent.h>
#include <KoColor.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include <kis_types.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <kis_cubic_curve.h>
#include "kis_display_color_converter.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_slider_spin_box.h"
#include "kis_canvas_resource_provider.h"
#include <recorder/kis_recorded_paint_action.h>
#include "kis_tool_utils.h"
#include <kis_paintop.h>
#include <kis_paintop_preset.h>
KisToolPaint::KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor)
: KisTool(canvas, cursor),
m_showColorPreview(false),
m_colorPreviewShowComparePlate(false),
m_colorPickerDelayTimer()
{
m_specialHoverModifier = false;
m_optionsWidgetLayout = 0;
m_opacity = OPACITY_OPAQUE_U8;
updateTabletPressureSamples();
m_supportOutline = false;
{
const int maxSize = 1000;
int brushSize = 1;
do {
m_standardBrushSizes.push_back(brushSize);
int increment = qMax(1, int(std::ceil(qreal(brushSize) / 15)));
brushSize += increment;
} while (brushSize < maxSize);
m_standardBrushSizes.push_back(maxSize);
}
KActionCollection *collection = this->canvas()->canvasController()->actionCollection();
if (!collection->action("increase_brush_size")) {
KAction *increaseBrushSize = new KAction(i18n("Increase Brush Size"), collection);
increaseBrushSize->setShortcut(Qt::Key_BracketRight);
collection->addAction("increase_brush_size", increaseBrushSize);
}
if (!collection->action("decrease_brush_size")) {
KAction *decreaseBrushSize = new KAction(i18n("Decrease Brush Size"), collection);
decreaseBrushSize->setShortcut(Qt::Key_BracketLeft);
collection->addAction("decrease_brush_size", decreaseBrushSize);
}
addAction("increase_brush_size", dynamic_cast<KAction*>(collection->action("increase_brush_size")));
addAction("decrease_brush_size", dynamic_cast<KAction*>(collection->action("decrease_brush_size")));
KisCanvas2 * kiscanvas = dynamic_cast<KisCanvas2*>(canvas);
if (kiscanvas && kiscanvas->viewManager()) {
connect(this, SIGNAL(sigPaintingFinished()), kiscanvas->viewManager()->resourceProvider(), SLOT(slotPainting()));
}
m_colorPickerDelayTimer.setSingleShot(true);
connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed()));
}
KisToolPaint::~KisToolPaint()
{
}
int KisToolPaint::flags() const
{
return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP;
}
void KisToolPaint::canvasResourceChanged(int key, const QVariant& v)
{
KisTool::canvasResourceChanged(key, v);
switch(key) {
case(KisCanvasResourceProvider::Opacity):
setOpacity(v.toDouble());
break;
default: //nothing
break;
}
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(updateTabletPressureSamples()), Qt::UniqueConnection);
}
void KisToolPaint::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
connect(actions().value("increase_brush_size"), SIGNAL(triggered()), SLOT(increaseBrushSize()), Qt::UniqueConnection);
connect(actions().value("decrease_brush_size"), SIGNAL(triggered()), SLOT(decreaseBrushSize()), Qt::UniqueConnection);
}
void KisToolPaint::deactivate()
{
disconnect(actions().value("increase_brush_size"), 0, this, 0);
disconnect(actions().value("decrease_brush_size"), 0, this, 0);
KisTool::deactivate();
}
QPainterPath KisToolPaint::tryFixBrushOutline(const QPainterPath &originalOutline)
{
KisConfig cfg;
if (cfg.newOutlineStyle() == OUTLINE_NONE) return originalOutline;
const qreal minThresholdSize = cfg.outlineSizeMinimum();
/**
* If the brush outline is bigger than the canvas itself (which
* would make it invisible for a user in most of the cases) just
* add a cross in the center of it
*/
QSize widgetSize = canvas()->canvasWidget()->size();
const int maxThresholdSum = widgetSize.width() + widgetSize.height();
QPainterPath outline = originalOutline;
QRectF boundingRect = outline.boundingRect();
const qreal sum = boundingRect.width() + boundingRect.height();
QPointF center = boundingRect.center();
if (sum > maxThresholdSum) {
const int hairOffset = 7;
outline.moveTo(center.x(), center.y() - hairOffset);
outline.lineTo(center.x(), center.y() + hairOffset);
outline.moveTo(center.x() - hairOffset, center.y());
outline.lineTo(center.x() + hairOffset, center.y());
} else if (sum < minThresholdSize && !outline.isEmpty()) {
outline = QPainterPath();
outline.addEllipse(center, 0.5 * minThresholdSize, 0.5 * minThresholdSize);
}
return outline;
}
void KisToolPaint::paint(QPainter &gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
QPainterPath path = tryFixBrushOutline(pixelToView(m_currentOutline));
paintToolOutline(&gc, path);
if (m_showColorPreview) {
QRectF viewRect = converter.documentToView(m_oldColorPreviewRect);
gc.fillRect(viewRect, m_colorPreviewCurrentColor);
if (m_colorPreviewShowComparePlate) {
QRectF baseColorRect = viewRect.translated(viewRect.width(), 0);
gc.fillRect(baseColorRect, m_colorPreviewBaseColor);
}
}
}
void KisToolPaint::setMode(ToolMode mode)
{
if(this->mode() == KisTool::PAINT_MODE &&
mode != KisTool::PAINT_MODE) {
// Let's add history information about recently used colors
emit sigPaintingFinished();
}
KisTool::setMode(mode);
}
void KisToolPaint::activatePickColor(AlternateAction action)
{
m_showColorPreview = true;
requestUpdateOutline(m_outlineDocPoint, 0);
int resource = colorPreviewResourceId(action);
KoColor color = canvas()->resourceManager()->koColorResource(resource);
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas);
m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color);
if (!m_colorPreviewBaseColor.isValid()) {
m_colorPreviewBaseColor = m_colorPreviewCurrentColor;
}
}
void KisToolPaint::deactivatePickColor(AlternateAction action)
{
Q_UNUSED(action);
m_showColorPreview = false;
m_oldColorPreviewRect = QRect();
m_oldColorPreviewUpdateRect = QRect();
m_colorPreviewCurrentColor = QColor();
}
void KisToolPaint::pickColorWasOverridden()
{
m_colorPreviewShowComparePlate = false;
m_colorPreviewBaseColor = QColor();
}
void KisToolPaint::activateAlternateAction(AlternateAction action)
{
switch (action) {
case PickFgNode:
case PickBgNode:
case PickFgImage:
case PickBgImage:
delayedAction = action;
m_colorPickerDelayTimer.start(100);
default:
pickColorWasOverridden();
KisTool::activateAlternateAction(action);
};
}
void KisToolPaint::activatePickColorDelayed()
{
switch (delayedAction) {
case PickFgNode:
useCursor(KisCursor::pickerLayerForegroundCursor());
activatePickColor(delayedAction);
break;
case PickBgNode:
useCursor(KisCursor::pickerLayerBackgroundCursor());
activatePickColor(delayedAction);
break;
case PickFgImage:
useCursor(KisCursor::pickerImageForegroundCursor());
activatePickColor(delayedAction);
break;
case PickBgImage:
useCursor(KisCursor::pickerImageBackgroundCursor());
activatePickColor(delayedAction);
break;
default:
break;
};
repaintDecorations();
}
void KisToolPaint::deactivateAlternateAction(AlternateAction action)
{
if (action != PickFgNode &&
action != PickBgNode &&
action != PickFgImage &&
action != PickBgImage) {
KisTool::deactivateAlternateAction(action);
return;
}
delayedAction = KisTool::NONE;
m_colorPickerDelayTimer.stop();
resetCursorStyle();
deactivatePickColor(action);
}
void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (pickColor(event->point, action)) {
setMode(SECONDARY_PAINT_MODE);
requestUpdateOutline(event->point, event);
} else {
KisTool::beginAlternateAction(event, action);
}
}
void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (!pickColor(event->point, action)) {
KisTool::continueAlternateAction(event, action);
} else {
requestUpdateOutline(event->point, event);
}
}
void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (pickColor(event->point, action)) {
setMode(KisTool::HOVER_MODE);
requestUpdateOutline(event->point, event);
} else {
KisTool::endAlternateAction(event, action);
}
}
int KisToolPaint::colorPreviewResourceId(AlternateAction action)
{
bool toForegroundColor = action == PickFgNode || action == PickFgImage;
int resource = toForegroundColor ?
KoCanvasResourceManager::ForegroundColor : KoCanvasResourceManager::BackgroundColor;
return resource;
}
bool KisToolPaint::pickColor(const QPointF &documentPixel,
AlternateAction action)
{
if (action != PickFgNode &&
action != PickBgNode &&
action != PickFgImage &&
action != PickBgImage) {
return false;
}
bool fromCurrentNode = action == PickFgNode || action == PickBgNode;
int resource = colorPreviewResourceId(action);
KisPaintDeviceSP device = fromCurrentNode ?
currentNode()->projection() : image()->projection();
QPoint imagePoint = image()->documentToIntPixel(documentPixel);
KoColor color;
QColor previewColor;
if (KisToolUtils::pickWrapped(device, imagePoint, &color, image())) {
canvas()->resourceManager()->setResource(resource, color);
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER(kisCanvas) { return true; }
previewColor = kisCanvas->displayColorConverter()->toQColor(color);
}
m_colorPreviewCurrentColor = previewColor;
m_colorPreviewShowComparePlate = true;
return true;
}
void KisToolPaint::mousePressEvent(KoPointerEvent *event)
{
KisTool::mousePressEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
void KisToolPaint::mouseMoveEvent(KoPointerEvent *event)
{
KisTool::mouseMoveEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
void KisToolPaint::mouseReleaseEvent(KoPointerEvent *event)
{
KisTool::mouseReleaseEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
QWidget * KisToolPaint::createOptionWidget()
{
QWidget * optionWidget = new QWidget();
optionWidget->setObjectName(toolId());
QVBoxLayout* verticalLayout = new QVBoxLayout(optionWidget);
verticalLayout->setObjectName("KisToolPaint::OptionWidget::VerticalLayout");
verticalLayout->setContentsMargins(0,0,0,0);
verticalLayout->setSpacing(1);
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(optionWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
verticalLayout->addWidget(specialSpacer);
verticalLayout->addWidget(specialSpacer);
m_optionsWidgetLayout = new QGridLayout();
m_optionsWidgetLayout->setColumnStretch(1, 1);
verticalLayout->addLayout(m_optionsWidgetLayout);
m_optionsWidgetLayout->setContentsMargins(0,0,0,0);
m_optionsWidgetLayout->setSpacing(1);
if (!quickHelp().isEmpty()) {
QPushButton* push = new QPushButton(koIcon("help-contents"), QString(), optionWidget);
connect(push, SIGNAL(clicked()), this, SLOT(slotPopupQuickHelp()));
QHBoxLayout* hLayout = new QHBoxLayout(optionWidget);
hLayout->addWidget(push);
hLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed));
verticalLayout->addLayout(hLayout);
}
return optionWidget;
}
QWidget* findLabelWidget(QGridLayout *layout, QWidget *control)
{
QWidget *result = 0;
int index = layout->indexOf(control);
int row, col, rowSpan, colSpan;
layout->getItemPosition(index, &row, &col, &rowSpan, &colSpan);
if (col > 0) {
QLayoutItem *item = layout->itemAtPosition(row, col - 1);
if (item) {
result = item->widget();
}
} else {
QLayoutItem *item = layout->itemAtPosition(row, col + 1);
if (item) {
result = item->widget();
}
}
return result;
}
void KisToolPaint::showControl(QWidget *control, bool value)
{
control->setVisible(value);
QWidget *label = findLabelWidget(m_optionsWidgetLayout, control);
if (label) {
label->setVisible(value);
}
}
void KisToolPaint::enableControl(QWidget *control, bool value)
{
control->setEnabled(value);
QWidget *label = findLabelWidget(m_optionsWidgetLayout, control);
if (label) {
label->setEnabled(value);
}
}
void KisToolPaint::addOptionWidgetLayout(QLayout *layout)
{
Q_ASSERT(m_optionsWidgetLayout != 0);
int rowCount = m_optionsWidgetLayout->rowCount();
m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2);
}
void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label)
{
Q_ASSERT(m_optionsWidgetLayout != 0);
if (label) {
m_optionsWidgetLayout->addWidget(label, m_optionsWidgetLayout->rowCount(), 0);
m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount() - 1, 1);
}
else {
m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount(), 0, 1, 2);
}
}
void KisToolPaint::setOpacity(qreal opacity)
{
m_opacity = quint8(opacity * OPACITY_OPAQUE_U8);
}
const KoCompositeOp* KisToolPaint::compositeOp()
{
if (currentNode()) {
KisPaintDeviceSP device = currentNode()->paintDevice();
if (device) {
QString op = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString();
return device->colorSpace()->compositeOp(op);
}
}
return 0;
}
void KisToolPaint::slotPopupQuickHelp()
{
QWhatsThis::showText(QCursor::pos(), quickHelp());
}
void KisToolPaint::updateTabletPressureSamples()
{
KisConfig cfg;
KisCubicCurve curve;
curve.fromString(cfg.pressureTabletCurve());
m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1);
}
void KisToolPaint::setupPaintAction(KisRecordedPaintAction* action)
{
KisTool::setupPaintAction(action);
action->setOpacity(m_opacity / qreal(255.0));
const KoCompositeOp* op = compositeOp();
if (op) {
action->setCompositeOp(op->id());
}
}
KisToolPaint::NodePaintAbility KisToolPaint::nodePaintAbility()
{
KisNodeSP node = currentNode();
if (!node || node->systemLocked()) {
return NONE;
}
if (node->inherits("KisShapeLayer")) {
return VECTOR;
}
if (node->paintDevice()) {
return PAINT;
}
return NONE;
}
void KisToolPaint::activatePrimaryAction()
{
pickColorWasOverridden();
setOutlineEnabled(true);
KisTool::activatePrimaryAction();
}
void KisToolPaint::deactivatePrimaryAction()
{
setOutlineEnabled(false);
KisTool::deactivatePrimaryAction();
}
bool KisToolPaint::isOutlineEnabled() const
{
return m_isOutlineEnabled;
}
void KisToolPaint::setOutlineEnabled(bool value)
{
m_isOutlineEnabled = value;
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::increaseBrushSize()
{
qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize().width();
std::vector<int>::iterator result =
std::upper_bound(m_standardBrushSizes.begin(),
m_standardBrushSizes.end(),
(int)paintopSize);
int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back();
qreal increment = newValue - paintopSize;
currentPaintOpPreset()->settings()->changePaintOpSize(increment, 0);
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::decreaseBrushSize()
{
qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize().width();
std::vector<int>::reverse_iterator result =
std::upper_bound(m_standardBrushSizes.rbegin(),
m_standardBrushSizes.rend(),
(int)paintopSize,
std::greater<int>());
int newValue = result != m_standardBrushSizes.rend() ? *result : m_standardBrushSizes.front();
qreal decrement = newValue - paintopSize;
currentPaintOpPreset()->settings()->changePaintOpSize(decrement, 0);
requestUpdateOutline(m_outlineDocPoint, 0);
}
QRectF KisToolPaint::colorPreviewDocRect(const QPointF &outlineDocPoint)
{
if (!m_showColorPreview) return QRect();
KisConfig cfg;
const QRectF colorPreviewViewRect = cfg.colorPreviewRect();
const QRectF colorPreviewDocumentRect = canvas()->viewConverter()->viewToDocument(colorPreviewViewRect);
return colorPreviewDocumentRect.translated(outlineDocPoint);
}
void KisToolPaint::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event)
{
if (!m_supportOutline) return;
KisConfig cfg;
KisPaintOpSettings::OutlineMode outlineMode;
outlineMode = KisPaintOpSettings::CursorNoOutline;
if (isOutlineEnabled() &&
(mode() == KisTool::GESTURE_MODE ||
((cfg.newOutlineStyle() == OUTLINE_FULL ||
cfg.newOutlineStyle() == OUTLINE_CIRCLE ||
cfg.newOutlineStyle() == OUTLINE_TILT ||
cfg.newOutlineStyle() == OUTLINE_COLOR ) &&
((mode() == HOVER_MODE) ||
(mode() == PAINT_MODE && cfg.showOutlineWhilePainting()))))) { // lisp forever!
if(cfg.newOutlineStyle() == OUTLINE_CIRCLE) {
outlineMode = KisPaintOpSettings::CursorIsCircleOutline;
} else if(cfg.newOutlineStyle() == OUTLINE_TILT) {
outlineMode = KisPaintOpSettings::CursorTiltOutline;
} else if(cfg.newOutlineStyle() == OUTLINE_COLOR) {
outlineMode = KisPaintOpSettings::CursorColorOutline;
} else {
outlineMode = KisPaintOpSettings::CursorIsOutline;
}
}
m_outlineDocPoint = outlineDocPoint;
m_currentOutline = getOutlinePath(m_outlineDocPoint, event, outlineMode);
QRectF outlinePixelRect = m_currentOutline.boundingRect();
QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect);
// This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path
// Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordiates
// See BUG 275829
qreal zoomX;
qreal zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
qreal xoffset = 2.0/zoomX;
qreal yoffset = 2.0/zoomY;
if (!outlineDocRect.isEmpty()) {
outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset);
}
QRectF colorPreviewDocRect = this->colorPreviewDocRect(m_outlineDocPoint);
QRectF colorPreviewDocUpdateRect;
if (!colorPreviewDocRect.isEmpty()) {
colorPreviewDocUpdateRect.adjust(-xoffset,-yoffset,xoffset,yoffset);
}
if (!m_oldColorPreviewUpdateRect.isEmpty()) {
canvas()->updateCanvas(m_oldColorPreviewUpdateRect);
}
if (!m_oldOutlineRect.isEmpty()) {
canvas()->updateCanvas(m_oldOutlineRect);
}
if (!outlineDocRect.isEmpty()) {
canvas()->updateCanvas(outlineDocRect);
}
if (!colorPreviewDocUpdateRect.isEmpty()) {
canvas()->updateCanvas(colorPreviewDocUpdateRect);
}
m_oldOutlineRect = outlineDocRect;
m_oldColorPreviewRect = colorPreviewDocRect;
m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect;
}
QPainterPath KisToolPaint::getOutlinePath(const QPointF &documentPos,
const KoPointerEvent *event,
KisPaintOpSettings::OutlineMode outlineMode)
{
Q_UNUSED(event);
QPointF imagePos = currentImage()->documentToPixel(documentPos);
QPainterPath path = currentPaintOpPreset()->settings()->
brushOutline(KisPaintInformation(imagePos), outlineMode);
return path;
}
diff --git a/krita/ui/tool/kis_tool_select_base.h b/krita/ui/tool/kis_tool_select_base.h
index 9ceb7710c52..c4824b6cfee 100644
--- a/krita/ui/tool/kis_tool_select_base.h
+++ b/krita/ui/tool/kis_tool_select_base.h
@@ -1,224 +1,224 @@
/* This file is part of the KDE project
* Copyright (C) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KISTOOLSELECTBASE_H
#define KISTOOLSELECTBASE_H
#include "KoPointerEvent.h"
#include "kis_tool.h"
#include "kis_canvas2.h"
#include "kis_selection.h"
#include "kis_selection_options.h"
#include "kis_selection_tool_config_widget_helper.h"
#include "KisViewManager.h"
#include "kis_selection_manager.h"
#include <bitset>
#define QMOD_BINARY() QString(std::bitset<sizeof(int) * 8>(event->modifiers()).to_string().c_str())
/**
* This is a basic template to create selection tools from basic path based drawing tools.
* The template overrides the ability to execute alternate actions correctly.
* Modifier keys are overridden with the following behavior:
*
* Shift: add to selection
* Alt: subtract from selection
* Shift+Alt: intersect current selection
* Ctrl: replace selection
*
* Certain tools also use modifier keys to alter their behavior, e.g. forcing square proportions with the rectangle tool.
* The template enables the following rules for forwarding keys:
* 1) Any modifier keys held *when the tool is first activated* will determine the new selection method.
* 2) If the underlying tool *does not take modifier keys*, pressing modifier keys in the middle of a stroke will change the selection method. This applies to the lasso tool and polygon tool.
* 3) If the underlying tool *takes modifier keys,* they will always be forwarded to the underlying tool, and it is not possible to change the selection method in the middle of a stroke.
*
*/
static SelectionAction selectionModifierMap(Qt::KeyboardModifiers m) {
SelectionAction newAction = SELECTION_DEFAULT;
if (m & Qt::ControlModifier) {
newAction = SELECTION_REPLACE;
} else if ((m & Qt::ShiftModifier) && (m & Qt::AltModifier)) {
newAction = SELECTION_INTERSECT;
} else if (m & Qt::ShiftModifier) {
newAction = SELECTION_ADD;
} else if (m & Qt::AltModifier) {
newAction = SELECTION_SUBTRACT;
}
return newAction;
}
template <class BaseClass>
class SelectionActionHandler : public BaseClass
{
public:
SelectionActionHandler(KoCanvasBase* canvas, const QString toolName)
:BaseClass(canvas),
m_widgetHelper(toolName),
m_selectionAction(SELECTION_DEFAULT),
m_selectionActionAlternate(SELECTION_DEFAULT)
{
}
SelectionActionHandler(KoCanvasBase* canvas, const QCursor cursor, const QString toolName)
:BaseClass(canvas, cursor),
m_widgetHelper(toolName),
m_selectionAction(SELECTION_DEFAULT),
m_selectionActionAlternate(SELECTION_DEFAULT)
{
}
SelectionActionHandler(KoCanvasBase* canvas, QCursor cursor, QString toolName, KisTool *delegateTool)
:BaseClass(canvas, cursor, delegateTool),
m_widgetHelper(toolName),
m_selectionAction(SELECTION_DEFAULT),
m_selectionActionAlternate(SELECTION_DEFAULT)
{
}
QWidget* createOptionWidget()
{
KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(this->canvas());
Q_ASSERT(canvas);
m_widgetHelper.createOptionWidget(canvas, this->toolId());
return m_widgetHelper.optionWidget();
}
void keyPressEvent(QKeyEvent *event)
{
if (!m_widgetHelper.processKeyPressEvent(event)) {
BaseClass::keyPressEvent(event);
}
}
SelectionMode selectionMode() const
{
return m_widgetHelper.selectionMode();
}
SelectionAction selectionAction() const
{
if (alternateSelectionAction() == SELECTION_DEFAULT) {
return m_widgetHelper.selectionAction();
}
return alternateSelectionAction();
}
bool antiAliasSelection() const
{
return m_widgetHelper.optionWidget()->antiAliasSelection();
}
SelectionAction alternateSelectionAction() const
{
return m_selectionActionAlternate;
}
KisSelectionOptions* selectionOptionWidget()
{
return m_widgetHelper.optionWidget();
}
virtual void setAlternateSelectionAction(SelectionAction action)
{
m_selectionActionAlternate = action;
- kDebug() << "Changing to selection action" << m_selectionActionAlternate;
+ dbgKrita << "Changing to selection action" << m_selectionActionAlternate;
}
void activateAlternateAction(KisTool::AlternateAction action)
{
Q_UNUSED(action);
BaseClass::activatePrimaryAction();
}
void deactivateAlternateAction(KisTool::AlternateAction action)
{
Q_UNUSED(action);
BaseClass::deactivatePrimaryAction();
}
void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
Q_UNUSED(action);
beginPrimaryAction(event);
}
void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
Q_UNUSED(action);
continuePrimaryAction(event);
}
void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) {
Q_UNUSED(action);
endPrimaryAction(event);
}
virtual void beginPrimaryAction(KoPointerEvent *event)
{
keysAtStart = event->modifiers();
setAlternateSelectionAction(selectionModifierMap(keysAtStart));
if (alternateSelectionAction() != SELECTION_DEFAULT) {
BaseClass::listenToModifiers(false);
}
BaseClass::beginPrimaryAction(event);
}
virtual void continuePrimaryAction(KoPointerEvent *event)
{
//If modifier keys have changed, tell the base tool it can start capturing modifiers
if ((keysAtStart != event->modifiers()) && !BaseClass::listeningToModifiers()) {
BaseClass::listenToModifiers(true);
}
//Always defer to the base class if it signals it is capturing modifier keys
if (!BaseClass::listeningToModifiers()) {
setAlternateSelectionAction(selectionModifierMap(event->modifiers()));
}
BaseClass::continuePrimaryAction(event);
}
void endPrimaryAction(KoPointerEvent *event)
{
keysAtStart = Qt::NoModifier; //reset this with each action
BaseClass::endPrimaryAction(event);
}
protected:
using BaseClass::canvas;
KisSelectionToolConfigWidgetHelper m_widgetHelper;
SelectionAction m_selectionAction;
SelectionAction m_selectionActionAlternate;
private:
Qt::KeyboardModifiers keysAtStart;
};
typedef SelectionActionHandler<KisTool> KisToolSelectBase;
#endif // KISTOOLSELECTBASE_H
diff --git a/krita/ui/widgets/kis_advanced_color_space_selector.cc b/krita/ui/widgets/kis_advanced_color_space_selector.cc
index 1e35c32cfad..72b1d8eddb2 100644
--- a/krita/ui/widgets/kis_advanced_color_space_selector.cc
+++ b/krita/ui/widgets/kis_advanced_color_space_selector.cc
@@ -1,746 +1,748 @@
/*
* Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
* Copyright (C) 2011 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
* Copyright (C) 2015 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_advanced_color_space_selector.h"
#include <KoFileDialog.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceEngine.h>
#include <KoColorModelStandardIds.h>
#include <KoID.h>
#include <KoConfig.h>
#include <KoIcon.h>
#ifdef GHNS
#include <knewstuff3/downloaddialog.h>
#include <knewstuff3/uploaddialog.h>
#endif
#include <QDesktopServices>
#include <QTextBrowser>
#include <QScrollBar>
#include <kcomponentdata.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <kurl.h>
#include "kis_factory2.h"
#include "ui_wdgcolorspaceselectoradvanced.h"
+#include <kis_debug.h>
+
struct KisAdvancedColorSpaceSelector::Private {
Ui_WdgColorSpaceSelectorAdvanced* colorSpaceSelector;
QString knsrcFile;
};
KisAdvancedColorSpaceSelector::KisAdvancedColorSpaceSelector(QWidget* parent, const QString &caption)
: QDialog(parent)
, d(new Private)
{
setWindowTitle(caption);
d->colorSpaceSelector = new Ui_WdgColorSpaceSelectorAdvanced;
d->colorSpaceSelector->setupUi(this);
d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible));
fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem());
d->colorSpaceSelector->bnDownloadProfile->setIcon(themedIcon("download"));
d->colorSpaceSelector->bnDownloadProfile->setToolTip( i18n("Download Color Profile") );
d->colorSpaceSelector->bnDownloadProfile->setEnabled( true );
d->colorSpaceSelector->bnDownloadProfile->hide();
d->colorSpaceSelector->bnUploadProfile->setIcon(themedIcon("arrow-up"));
d->colorSpaceSelector->bnUploadProfile->setToolTip( i18n("Share Color Profile") );
d->colorSpaceSelector->bnUploadProfile->setEnabled( false );
d->colorSpaceSelector->bnUploadProfile->hide();
#ifdef GHNS
d->colorSpaceSelector->bnUploadProfile->show();
d->colorSpaceSelector->bnDownloadProfile->show();
#endif
d->colorSpaceSelector->bnInstallProfile->setIcon(themedIcon("document-open"));
d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") );
connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)),
this, SLOT(fillCmbDepths(const KoID &)));
connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)),
this, SLOT(fillLstProfiles()));
connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)),
this, SLOT(fillLstProfiles()));
connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
this, SLOT(colorSpaceChanged()));
connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
this, SLOT(buttonUpdate()));
connect(this, SIGNAL(selectionChanged(bool)),
this, SLOT(fillDescription()));
connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TongueWidget, SLOT(repaint()));
connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile()));
connect(d->colorSpaceSelector->bnDownloadProfile, SIGNAL(clicked()), this, SLOT(downloadProfile()));
connect(d->colorSpaceSelector->bnUploadProfile, SIGNAL(clicked()), this, SLOT(uploadProfile()));
connect(d->colorSpaceSelector->bnOK, SIGNAL(accepted()), this, SLOT(accept()));
connect(d->colorSpaceSelector->bnOK, SIGNAL(rejected()), this, SLOT(reject()));
d->knsrcFile = "kritaiccprofiles.knsrc";
fillLstProfiles();
}
KisAdvancedColorSpaceSelector::~KisAdvancedColorSpaceSelector()
{
delete d->colorSpaceSelector;
delete d;
}
void KisAdvancedColorSpaceSelector::fillLstProfiles()
{
d->colorSpaceSelector->lstProfile->blockSignals(true);
QString s = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem());
d->colorSpaceSelector->lstProfile->clear();
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s);
if (csf == 0) return;//TODO: make this give better feedback.
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
QStringList profileNames;
foreach(const KoColorProfile *profile, profileList) {
profileNames.append(profile->name());
}
qSort(profileNames);
QListWidgetItem *defaultProfile = new QListWidgetItem;
defaultProfile->setText(csf->defaultProfile() + " " + i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)"));
foreach(QString stringName, profileNames) {
if (stringName==csf->defaultProfile()) {
d->colorSpaceSelector->lstProfile->addItem(defaultProfile);
} else {
d->colorSpaceSelector->lstProfile->addItem(stringName);
}
}
d->colorSpaceSelector->lstProfile->setCurrentItem(defaultProfile);
d->colorSpaceSelector->lstProfile->blockSignals(false);
colorSpaceChanged();
}
void KisAdvancedColorSpaceSelector::fillCmbDepths(const KoID& id)
{
KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem();
d->colorSpaceSelector->cmbColorDepth->clear();
QList<KoID> depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible);
QList<KoID> sortedDepths;
if (depths.contains(Integer8BitsColorDepthID)) {
sortedDepths << Integer8BitsColorDepthID;
}
if (depths.contains(Integer16BitsColorDepthID)) {
sortedDepths << Integer16BitsColorDepthID;
}
if (depths.contains(Float16BitsColorDepthID)) {
sortedDepths << Float16BitsColorDepthID;
}
if (depths.contains(Float32BitsColorDepthID)) {
sortedDepths << Float32BitsColorDepthID;
}
if (depths.contains(Float64BitsColorDepthID)) {
sortedDepths << Float64BitsColorDepthID;
}
d->colorSpaceSelector->cmbColorDepth->setIDList(sortedDepths);
if (sortedDepths.contains(activeDepth)) {
d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth);
}
}
void KisAdvancedColorSpaceSelector::fillDescription()
{
QString notApplicable = i18nc("Not Applicable, used where there's no colorants or gamma curve found","N/A");
QString notApplicableTooltip = i18nc("@info:tooltip","This profile has no colorants.");
QString profileName = i18nc("Shows up instead of the name when there's no profile","No Profile Found");
QString whatIsColorant = i18n("Colorant in d50-adapted xyY.");
//set colorants
QString s = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem());
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s);
if (csf == 0) return;
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
if (profileList.isEmpty()==false) {
profileName = currentColorSpace()->profile()->name();
if (currentColorSpace()->profile()->hasColorants()){
QVector <double> colorants = currentColorSpace()->profile()->getColorantsxyY();
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
//QString text = currentColorSpace()->profile()->info() + " =" +
d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint));
d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint[0], 'f', 4) + ", " + QString::number(whitepoint[1], 'f', 4) + ", " + QString::number(whitepoint[2], 'f', 4));
d->colorSpaceSelector->lblXYZ_R->setText(QString::number(colorants[0], 'f', 4) + ", " + QString::number(colorants[1], 'f', 4) + ", " + QString::number(colorants[2], 'f', 4));
d->colorSpaceSelector->lblXYZ_G->setText(QString::number(colorants[3], 'f', 4) + ", " + QString::number(colorants[4], 'f', 4) + ", " + QString::number(colorants[5], 'f', 4));
d->colorSpaceSelector->lblXYZ_B->setText(QString::number(colorants[6], 'f', 4) + ", " + QString::number(colorants[7], 'f', 4) + ", " + QString::number(colorants[8], 'f', 4));
d->colorSpaceSelector->lblXYZ_R->setToolTip(whatIsColorant);
d->colorSpaceSelector->lblXYZ_G->setToolTip(whatIsColorant);
d->colorSpaceSelector->lblXYZ_B->setToolTip(whatIsColorant);
} else {
QVector <double> whitepoint2 = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint2));
d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint2[0], 'f', 4) + ", " + QString::number(whitepoint2[1], 'f', 4) + ", " + QString::number(whitepoint2[2], 'f', 4));
d->colorSpaceSelector->lblXYZ_R->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_R->setToolTip(notApplicableTooltip);
d->colorSpaceSelector->lblXYZ_G->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_G->setToolTip(notApplicableTooltip);
d->colorSpaceSelector->lblXYZ_B->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_B->setToolTip(notApplicableTooltip);
}
} else {
d->colorSpaceSelector->lblXYZ_W->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_W->setToolTip(notApplicableTooltip);
d->colorSpaceSelector->lblXYZ_R->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_R->setToolTip(notApplicableTooltip);
d->colorSpaceSelector->lblXYZ_G->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_G->setToolTip(notApplicableTooltip);
d->colorSpaceSelector->lblXYZ_B->setText(notApplicable);
d->colorSpaceSelector->lblXYZ_B->setToolTip(notApplicableTooltip);
}
//set TRC
QVector <double> estimatedTRC(3);
QString estimatedGamma = i18nc("Estimated Gamma indicates how the TRC (Tone Response Curve or Tone Reproduction Curve) is bent. A Gamma of 1.0 means linear.", "<b>Estimated Gamma</b>: ");
QString estimatedsRGB = i18nc("This is for special Gamma types that LCMS cannot differentiate between", "<b>Estimated Gamma</b>: sRGB, L* or rec709 TRC");
QString whatissRGB = i18nc("@info:tooltip","The Tone Response Curve of this color space is either sRGB, L* or rec709 TRC.");
QString currentModelStr = d->colorSpaceSelector->cmbColorModels->currentItem().id();
if (profileList.isEmpty()) {
d->colorSpaceSelector->TongueWidget->setProfileDataAvailable(false);
}
else if (currentModelStr == "RGBA") {
QVector <double> colorants = currentColorSpace()->profile()->getColorantsxyY();
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants);
estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC();
if (estimatedTRC[0] == -1) {
d->colorSpaceSelector->lbltrc->setToolTip(whatissRGB);
d->colorSpaceSelector->lbltrc->setText(estimatedsRGB);
} else {
d->colorSpaceSelector->lbltrc->setToolTip(estimatedGamma + QString::number(estimatedTRC[0]) + "," + QString::number(estimatedTRC[1]) + "," + QString::number(estimatedTRC[2]));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + QString::number((estimatedTRC[0] + estimatedTRC[1] + estimatedTRC[2])/3));
}
}
else if (currentModelStr == "GRAYA") {
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setGrayData(whitepoint);
estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC();
if (estimatedTRC[0] == -1) {
d->colorSpaceSelector->lbltrc->setToolTip(whatissRGB);
d->colorSpaceSelector->lbltrc->setText(estimatedsRGB);
} else {
d->colorSpaceSelector->lbltrc->setToolTip(estimatedGamma + QString::number(estimatedTRC[0]));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + QString::number(estimatedTRC[0]));
}
}
else if (currentModelStr == "CMYKA") {
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setCMYKData(whitepoint);
d->colorSpaceSelector->lbltrc->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for CMYK."));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + notApplicable);
}
else if (currentModelStr == "XYZA") {
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setXYZData(whitepoint);
d->colorSpaceSelector->lbltrc->setToolTip(i18nc("@info:tooltip","XYZ is assumed to be linear Gamma."));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + "1.0");
}
else if (currentModelStr == "LABA") {
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setLABData(whitepoint);
d->colorSpaceSelector->lbltrc->setToolTip(i18nc("@info:tooltip","This is assumed to be the L * TRC."));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + "L*");
}
else if (currentModelStr == "YCbCrA") {
QVector <double> whitepoint = currentColorSpace()->profile()->getWhitePointxyY();
d->colorSpaceSelector->TongueWidget->setYCbCrData(whitepoint);
d->colorSpaceSelector->lbltrc->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for YCrCb."));
d->colorSpaceSelector->lbltrc->setText(estimatedGamma + notApplicable);
}
d->colorSpaceSelector->textProfileDescription->clear();
d->colorSpaceSelector->textProfileDescription->append("<h3>About " + currentColorSpace()->name() + "/" + profileName + "</h3>");
if (currentModelStr == "RGBA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is RGB",
"<b><a href=\"https://en.wikipedia.org/wiki/RGB_color_space\">RGB (Red, Green, Blue)</a></b>, is the color model used by screens and other light-based media.<br/>"
"RGB is an additive color model: adding colors together makes them brighter. This color "
"model is the most extensive of all color models, and is recommended as a model for painting,"
"that you can later convert to other spaces. RGB is also the recommended colorspace for HDR editing."));
} else if (currentModelStr == "CMYKA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is CMYK",
"<b><a href=\"https://en.wikipedia.org/wiki/CMYK_color_model\">CMYK (Cyan, Magenta, Yellow, Key)</a></b>, "
"is the model used by printers and other ink-based media.<p>"
"CMYK is a subtractive model, meaning that adding colors together will turn them darker. Because of CMYK "
"profiles being very specific per printer, it is recommended to work in RGB space, and then later convert "
"to a CMYK profile, preferably one delivered by your printer. <br/>"
"CMYK is <b>not</b> recommended for painting."
"Unfortunately, Krita cannot retrieve colorants or the TRC for this space."));
} else if (currentModelStr == "XYZA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is XYZ",
"<b><a href=\"https://en.wikipedia.org/wiki/CIE_1931_color_space\">CIE XYZ</a></b>"
"is the space determined by the CIE as the space that encompasses all other colors, and used to "
"convert colors between profiles. XYZ is an additive color model, meaning that adding colors together "
"makes them brighter. XYZ is <b>not</b> recommended for painting, but can be useful to encode in. The Tone Response "
"Curve is assumed to be linear."));
} else if (currentModelStr == "GRAYA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is Grayscale",
"<b><a href=\"https://en.wikipedia.org/wiki/Grayscale\">Grayscale</a></b> only allows for "
"gray values and transparent values. Grayscale images use half "
"the memory and disk space compared to an RGB image of the same bit-depth.<br/>"
"Grayscale is useful for inking and greyscale images. In "
"Krita, you can mix Grayscale and RGB layers in the same image."));
} else if (currentModelStr == "LABA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is LAB",
"<b><a href=\"https://en.wikipedia.org/wiki/Lab_color_space\">L*a*b</a></b>. <b>L<b> stands for Lightness, "
"the <b>a</b> and <b>b</b> components represent color channels.<br/>"
"L*a*b is a special model for color correction. It is based on human perception, meaning that it "
"tries to encode the difference in lightness, red-green balance and yellow-blue balance. "
"This makes it useful for color correction, but the vast majority of color maths in the blending "
"modes do <b>not</b> work as expected here.<br/>"
"Similarly, Krita does not support HDR in LAB, meaning that HDR images converted to LAB lose color "
"information. This colorspace is <b>not</b> recommended for painting, nor for export, "
"but best as a space to do post-processing in. The Tone Response Curve is assumed to be the L* TRC."));
} else if (currentModelStr == "YCbCrA") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("If the selected model is YCbCr",
"<b><a href=\"https://en.wikipedia.org/wiki/YCbCr\">YCbCr (Luma, Blue Chroma, Red Chroma)</a></b>, is a "
"model designed for video encoding. It is based on human perception, meaning that it tries to "
"encode the difference in lightness, red-green balance and yellow-blue balance. Chroma in "
"this case is then a word indicating a special type of saturation, in these cases the saturation "
"of Red and Blue, of which the desaturated equivalents are Green and Yellow respectively. It "
"is available to open up certain images correctly, but Krita does not currently ship a profile for "
"this due to lack of open source ICC profiles for YCrCb."));
}
QString currentDepthStr = d->colorSpaceSelector->cmbColorDepth->currentItem().id();
if (currentDepthStr == "U8") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("When the selected Bitdepth is 8",
"<b>8 bit integer</b>: The default amount of colors per channel. Each channel will have 256 values available, "
"leading to a total amount of 256*amount of channels. Recommended to use for images intended for the web, "
"or otherwise simple images."));
}
else if (currentDepthStr == "U16") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("When the selected Bitdepth is 16",
"<b>16 bit integer</b>: Also known as 'deep color'. 16 bit is ideal for editing images with a linear TRC, large "
"color space, or just when you need more precise color blending. This does take twice as much space on "
"the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it "
"takes much more processing power. We recommend watching the RAM usage of the file carefully, or "
"otherwise use 8 bit if your computer slows down. Take care to disable conversion optimization "
"when converting from 16 bit/channel to 8 bit/channel."));
}
else if (currentDepthStr == "F16") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("When the selected Bitdepth is 16 bit float",
"<b>16 bit floating point</b>: Also known as 'Half Floating Point', and the standard in VFX industry images. "
"16 bit float is ideal for editing images with a linear Tone Response Curve, large color space, or just when you need "
"more precise color blending. It being floating point is an absolute requirement for Scene Referred "
"(HDR) images. This does take twice as much space on the RAM and hard-drive than any given 8 bit image "
"of the same properties, and for some devices it takes much more processing power. We recommend watching "
"the RAM usage of the file carefully, or otherwise use 8 bit if your computer slows down."));
}
else if (currentDepthStr == "F32") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("When the selected Bitdepth is 32bit float",
"<b>32 bit float point</b>: Also known as 'Full Floating Point'. 32 bit float is ideal for editing images "
"with a linear TRC, large color space, or just when you need more precise color blending. It being "
"floating point is an absolute requirement for Scene Referred (HDR) images. This does take four times "
"as much space on the RAM and hard-drive than any given 8 bit image of the same properties, and for "
"some devices it takes much more processing power. We recommend watching the RAM usage of the file "
"carefully, or otherwise use 8 bit if your computer slows down."));
}
else if (currentDepthStr == "F64") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("When the selected Bitdepth is 64bit float, but this isn't actually available in Krita at the moment.",\
"<b>64 bit float point</b>: 64 bit float is as precise as it gets in current technology, and this depth is used "
"most of the time for images that are generated or used as an input for software. It being floating point "
"is an absolute requirement for Scene Referred (HDR) images. This does take eight times as much space on "
"the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it takes "
"much more processing power. We recommend watching the RAM usage of the file carefully, or otherwise use "
"8 bit if your computer slows down."));
}
if (profileName.contains("-elle-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("These are Elle Stone's notes on her profiles that we ship.",
"<p><b>Extra notes on profiles by Elle Stone:</b>"
"<p><i>Krita comes with a number of high quality profiles created by "
"<a href=\"http://ninedegreesbelow.com\">Elle Stone</a>. This is a summary. Please check "
"<a href=\"http://ninedegreesbelow.com/photography/lcms-make-icc-profiles.html\">the full documentation</a> as well.</i>"));
if (profileName.contains("ACES-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>Quoting Wikipedia, 'Academy Color Encoding System (ACES) is a color image "
"encoding system proposed by the Academy of Motion Picture Arts and Sciences that will allow for "
"a fully encompassing color accurate workflow, with 'seamless interchange of high quality motion "
"picture images regardless of source'."));
}
if (profileName.contains("ACEScg-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>The ACEScg color space is smaller than the ACES color space, but large enough to contain the 'Rec-2020 gamut "
"and the DCI-P3 gamut', unlike the ACES color space it has no negative values and contains only few colors "
"that fall just barely outside the area of real colors humans can see"));
}
if (profileName.contains("ClayRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"To avoid possible copyright infringement issues, I used 'ClayRGB' (following ArgyllCMS) as the base name "
"for these profiles. As used below, 'Compatible with Adobe RGB 1998' is terminology suggested in the preamble "
"to the AdobeRGB 1998 color space specifications.<p>"
"The Adobe RGB 1998 color gamut covers a higher "
"percentage of real-world cyans, greens, and yellow-greens than sRGB, but still doesn't include all printable "
"cyans, greens, yellow-greens, especially when printing using today's high-end, wider gamut, ink jet printers. "
"BetaRGB (not included in the profile pack) and Rec.2020 are better matches for the color gamuts of today's "
"wide gamut printers.<p>"
"The Adobe RGB 1998 color gamut is a reasonable approximation to some of today's "
"high-end wide gamut monitors."));
}
if (profileName.contains("AllColorsRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"This profile's color gamut is roughly the same size and shape as the ACES color space gamut, "
"and like the ACES color space, AllColorsRGB holds all possible real colors. But AllColorsRGB "
"actually has a slightly larger color gamut (to capture some fringe colors that barely qualify "
"as real when viewed by the standard observer) and uses the D50 white point.<p>"
"Just like the ACES color space, AllColorsRGB holds a high percentage of imaginary colors. See the Completely "
"<a href=\"http://ninedegreesbelow.com/photography/xyz-rgb.html\">"
"Painless Programmer's Guide to XYZ, RGB, ICC, xyY, and TRCs</a> for more information about imaginary "
"colors.<p>"
"There is no particular reason why anyone would want to use this profile "
"for editing, unless one needs to make sure your color space really does hold all "
"possible real colors."));
}
if (profileName.contains("CIERGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"This profile is included mostly for its historical significance. "
"It's the color space that was used in the original color matching experiments "
"that led to the creation of the XYZ reference color space.<p>"
"The ASTM E white point "
"is probably the right E white point to use when making the CIERGB color space profile. "
"It's not clear to me what the correct CIERGB primaries really are. "
"Lindbloom gives one set. The LCMS version 1 tutorial gives a different set. "
"Experts in the field contend that the real primaries "
"should be calculated from the spectral wavelengths, so I did."));
}
if (profileName.contains("IdentityRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"The IdentityRGB working space is included in the profile pack because it's a mathematically "
"obvious way to include all possible visible colors, though it has a higher percentage of "
"imaginary colors than the ACES and AllColorsRGB color spaces. I cannot think of any reason "
"why you'd ever want to actually edit images in the IdentityRGB working space."));
}
if (profileName.contains("LargeRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"To avoid possible copyright infringement issues, I used 'LargeRGB' (following RawTherapee) "
"as the base name for these profiles.<p>"
"Kodak designed the RIMM/ROMM (ProPhotoRGB) color "
"gamut to include all printable and most real world colors. It includes some imaginary colors "
"and excludes some of the real world blues and violet blues that can be captured by digital "
"cameras. It also excludes some very saturated 'camera-captured' yellows as interpreted by "
"some (and probably many) camera matrix input profiles.<p>"
"The ProPhotoRGB primaries are "
"hard-coded into Adobe products such as Lightroom and the Dng-DCP camera 'profiles'. However, "
"other than being large enough to hold a lot of colors, ProPhotoRGB has no particular merit "
"as an RGB working space. Personally and for most editing purposes, I recommend BetaRGB, Rec2020, "
"or the ACEScg profiles ProPhotoRGB."));
}
if (profileName.contains("Rec2020-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"Rec.2020 is the up-and-coming replacement for the thoroughly outdated sRGB color space. As of "
"June 2015, very few (if any) display devices (and certainly no affordable display devices) can "
"display all of Rec.2020. However, display technology is closing in on Rec.2020, movies are "
"already being made for Rec.2020, and various cameras offer support for Rec.2020. And in the "
"digital darkroom Rec.2020 is much more suitable as a general RGB working space than the "
"exceedingly small sRGB color space."));
}
if (profileName.contains("sRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"Hewlett-Packard and Microsoft designed sRGB to match the color gamut of consumer-grade CRTs "
"from the 1990s. sRGB is the standard color space for the world wide web and is still the best "
"choice for exporting images to the internet.<p>"
"The sRGB color gamut was a good match to "
"calibrated decent quality CRTs. But sRGB is not a good match to many consumer-grade LCD monitors, "
"which often cannot display the more saturated sRGB blues and magentas (the good news: as technology "
"progresses, wider gamuts are trickling down to consumer grade monitors).<p>"
"Printer color gamuts can easily exceed the sRGB color gamut in cyans, greens, and yellow-greens. Colors from interpolated "
"camera raw files also often exceed the sRGB color gamut.<p>"
"As a very relevant aside, using perceptual "
"intent when converting to sRGB does not magically makes otherwise out of gamut colors fit inside the "
"sRGB color gamut! The standard sRGB color space (along with all the other the RGB profiles provided "
"in my profile pack) is a matrix profile, and matrix profiles don't have perceptual intent tables.</p>"));
}
if (profileName.contains("WideRGB-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"To avoid possible copyright infringement issues, I used 'WideRGB' as the base name for these profiles.<p>"
"WideGamutRGB was designed by Adobe to be a wide gamut color space that uses spectral colors "
"as its primaries. Pascale's primary values produce a profile that matches old V2 Widegamut profiles "
"from Adobe and Canon. It is an interesting color space, but shortly after its introduction, Adobe "
"switched their emphasis to the ProPhotoRGB color space."));
}
if (profileName.contains("Gray-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>These profiles are for use with RGB images that have been converted to monotone gray (black and white). "
"The main reason to convert from RGB to Gray is to save the file space needed to encode the image. "
"Google places a premium on fast-loading web pages, and images are one of the slower-loading elements "
"of a web page. So converting black and white images to Grayscale images does save some kilobytes. "
" For grayscale images uploaded to the internet, convert the image to the V2 Gray profile with the sRGB TRC."));
}
if (profileName.contains("-g10")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>The profiles that end in '-g10.icc' are linear gamma (gamma=1.0, 'linear light', etc) profiles and "
"should only be used when editing at high bit depths (16-bit floating point, 16-bit integer, 32-bit "
"floating point, 32-bit integer). Many editing operations produce better results in linear gamma color "
"spaces."));
}
if (profileName.contains("-labl")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>The profiles that end in '-labl.icc' have perceptually uniform TRCs. A few editing operations really "
"should be done on perceptually uniform RGB. Make sure you use the V4 versions for editing high bit depth "
"images."));
}
if (profileName.contains("-srgbtrc") || profileName.contains("-g22") || profileName.contains("-g18") || profileName.contains("-bt709")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>The profiles that end in '-srgbtrc.icc', '-g22.icc', and '-bt709.icc' have approximately but not exactly "
"perceptually uniform TRCs. ProPhotoRGB's gamma=1.8 TRC is not quite as close to being perceptually uniform."));
}
if (d->colorSpaceSelector->cmbColorDepth->currentItem().id()=="U8") {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>When editing 8-bit images, you should use a profile with a small color gamut and an approximately or "
"exactly perceptually uniform TRC. Of the profiles supplied in my profile pack, only the sRGB and AdobeRGB1998 "
"(ClayRGB) color spaces are small enough for 8-bit editing. Even with the AdobeRGB1998 color space you need to "
"be careful to not cause posterization. And of course you cannot use the linear gamma versions of these profiles "
"for 8-bit editing."));
}
if (profileName.contains("-V4-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>Use V4 profiles for editing images using high bit depth image editors that use LCMS as the Color Management Module. "
"This includes Krita, digiKam/showFoto, and GIMP 2.9."));
}
if (profileName.contains("-V2-")) {
d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.",
"<p>Use V2 profiles for exporting finished images to be uploaded to the web or for use with imaging software that "
"cannot read V4 profiles."));
}
}
d->colorSpaceSelector->textProfileDescription->moveCursor(QTextCursor::Start);
}
QString KisAdvancedColorSpaceSelector::nameWhitePoint(QVector <double> whitePoint) {
QString name=(QString::number(whitePoint[0]) + ", " + QString::number(whitePoint[1], 'f', 4));
//A (0.451170, 0.40594) (2856K)(tungsten)
if ((whitePoint[0]>0.451170-0.005 && whitePoint[0]<0.451170 + 0.005) &&
(whitePoint[1]>0.40594-0.005 && whitePoint[1]<0.40594 + 0.005)){
name="A";
return name;
}
//B (0.34980, 0.35270) (4874K) (Direct Sunlight at noon)(obsolete)
//C (0.31039, 0.31905) (6774K) (avarage/north sky daylight)(obsolete)
//D50 (0.34773, 0.35952) (5003K) (Horizon Light, default color of white paper, ICC profile standard illuminant)
if ((whitePoint[0]>0.34773-0.005 && whitePoint[0]<0.34773 + 0.005) &&
(whitePoint[1]>0.35952-0.005 && whitePoint[1]<0.35952 + 0.005)){
name="D50";
return name;
}
//D55 (0.33411, 0.34877) (5503K) (Mid-morning / Mid-afternoon Daylight)
if ((whitePoint[0]>0.33411-0.001 && whitePoint[0]<0.33411 + 0.001) &&
(whitePoint[1]>0.34877-0.005 && whitePoint[1]<0.34877 + 0.005)){
name="D55";
return name;
}
//D60 (0.3217, 0.3378) (~6000K) (ACES colorspace default)
if ((whitePoint[0]>0.3217-0.001 && whitePoint[0]<0.3217 + 0.001) &&
(whitePoint[1]>0.3378-0.005 && whitePoint[1]<0.3378 + 0.005)){
name="D60";
return name;
}
//D65 (0.31382, 0.33100) (6504K) (Noon Daylight, default for computer and tv screens, sRGB default)
//Elle's are old school with 0.3127 and 0.3289
if ((whitePoint[0]>0.31382-0.002 && whitePoint[0]<0.31382 + 0.002) &&
(whitePoint[1]>0.33100-0.005 && whitePoint[1]<0.33100 + 0.002)){
name="D65";
return name;
}
//D75 (0.29968, 0.31740) (7504K) (North sky Daylight)
if ((whitePoint[0]>0.29968-0.001 && whitePoint[0]<0.29968 + 0.001) &&
(whitePoint[1]>0.31740-0.005 && whitePoint[1]<0.31740 + 0.005)){
name="D75";
return name;
}
//E (1/3, 1/3) (5454K) (Equal Energy. CIERGB default)
if ((whitePoint[0]>(1.0/3.0)-0.001 && whitePoint[0]<(1.0/3.0) + 0.001) &&
(whitePoint[1]>(1.0/3.0)-0.001 && whitePoint[1]<(1.0/3.0) + 0.001)){
name="E";
return name;
}
//The F series seems to sorta overlap with the D series, so I'll just leave them in comment here.//
//F1 (0.31811, 0.33559) (6430K) (Daylight Fluorescent)
//F2 (0.37925, 0.36733) (4230K) (Cool White Fluorescent)
//F3 (0.41761, 0.38324) (3450K) (White Florescent)
//F4 (0.44920, 0.39074) (2940K) (Warm White Fluorescent)
//F5 (0.31975, 0.34246) (6350K) (Daylight Fluorescent)
//F6 (0.38660, 0.37847) (4150K) (Lite White Fluorescent)
//F7 (0.31569, 0.32960) (6500K) (D65 simulator, Daylight simulator)
//F8 (0.34902, 0.35939) (5000K) (D50 simulator)
//F9 (0.37829, 0.37045) (4150K) (Cool White Deluxe Fluorescent)
//F10 (0.35090, 0.35444) (5000K) (Philips TL85, Ultralume 50)
//F11 (0.38541, 0.37123) (4000K) (Philips TL84, Ultralume 40)
//F12 (0.44256, 0.39717) (3000K) (Philips TL83, Ultralume 30)
return name;
}
const KoColorSpace* KisAdvancedColorSpaceSelector::currentColorSpace()
{
QString check = "";
if (d->colorSpaceSelector->lstProfile->currentItem()) {
check = d->colorSpaceSelector->lstProfile->currentItem()->text();
} else {
check = d->colorSpaceSelector->lstProfile->item(0)->text();
}
return KoColorSpaceRegistry::instance()->colorSpace(d->colorSpaceSelector->cmbColorModels->currentItem().id(),
d->colorSpaceSelector->cmbColorDepth->currentItem().id(),
check);
}
void KisAdvancedColorSpaceSelector::setCurrentColorModel(const KoID& id)
{
d->colorSpaceSelector->cmbColorModels->setCurrent(id);
fillLstProfiles();
fillCmbDepths(id);
}
void KisAdvancedColorSpaceSelector::setCurrentColorDepth(const KoID& id)
{
d->colorSpaceSelector->cmbColorDepth->setCurrent(id);
fillLstProfiles();
}
void KisAdvancedColorSpaceSelector::setCurrentProfile(const QString& name)
{
QList<QListWidgetItem *> Items= d->colorSpaceSelector->lstProfile->findItems(name, Qt::MatchStartsWith);
d->colorSpaceSelector->lstProfile->setCurrentItem(Items.at(0));
}
void KisAdvancedColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace)
{
setCurrentColorModel(colorSpace->colorModelId());
setCurrentColorDepth(colorSpace->colorDepthId());
setCurrentProfile(colorSpace->profile()->name());
}
void KisAdvancedColorSpaceSelector::colorSpaceChanged()
{
bool valid = d->colorSpaceSelector->lstProfile->count() != 0;
emit(selectionChanged(valid));
if (valid) {
emit colorSpaceChanged(currentColorSpace());
}
}
void KisAdvancedColorSpaceSelector::installProfile()
{
QStringList mime;
mime << "*.icm" << "*.icc";
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC");
dialog.setCaption(i18n("Install Color Profiles"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
dialog.setNameFilters(mime);
QStringList profileNames = dialog.urls();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
QString saveLocation = KGlobal::dirs()->saveLocation("icc_profiles");
foreach (const QString &profileName, profileNames) {
KUrl file(profileName);
if (!QFile::copy(profileName, saveLocation + file.fileName())) {
- kWarning() << "Could not install profile!";
+ dbgKrita << "Could not install profile!";
return;
}
iccEngine->addProfile(saveLocation + file.fileName());
}
fillLstProfiles();
}
void KisAdvancedColorSpaceSelector::downloadProfile()
{
#ifdef GHNS
KNS3::DownloadDialog dialog( "kritaiccprofiles.knsrc", this);
dialog.exec();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
foreach (const KNS3::Entry& e, dialog.changedEntries()) {
foreach(const QString &file, e.installedFiles()) {
QFileInfo fi(file);
iccEngine->addProfile( fi.absolutePath() + '/' + fi.fileName());
}
foreach(const QString &file, e.uninstalledFiles()) {
QFileInfo fi(file);
iccEngine->removeProfile( fi.absolutePath() + '/' + fi.fileName());
}
}
fillLstProfiles();
#endif
}
void KisAdvancedColorSpaceSelector::uploadProfile()
{
#ifdef GHNS
KNS3::UploadDialog dialog("kritaiccprofiles.knsrc", this);
const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(d->colorSpaceSelector->lstProfile->currentText());
if (!profile) return;
dialog.setUploadFile(KUrl::fromLocalFile(profile->fileName()));
dialog.setUploadName(profile->name());
dialog.exec();
#endif
}
void KisAdvancedColorSpaceSelector::buttonUpdate()
{
QString check = "";
if (d->colorSpaceSelector->lstProfile->currentItem()) {
check = d->colorSpaceSelector->lstProfile->currentItem()->text();
} else {
check = d->colorSpaceSelector->lstProfile->item(0)->text();
}
const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(check);
if (!profile) return;
QFileInfo fileInfo(profile->fileName());
if (fileInfo.isWritable()) {
d->colorSpaceSelector->bnUploadProfile->setEnabled( true );
return;
}
d->colorSpaceSelector->bnUploadProfile->setEnabled( false );
}
diff --git a/krita/ui/widgets/kis_color_space_selector.cc b/krita/ui/widgets/kis_color_space_selector.cc
index bef0f2647b4..37b9200eb3d 100644
--- a/krita/ui/widgets/kis_color_space_selector.cc
+++ b/krita/ui/widgets/kis_color_space_selector.cc
@@ -1,300 +1,301 @@
/*
* Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
* Copyright (C) 2011 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_color_space_selector.h"
#include <kglobal.h>
#include <kurl.h>
#include <KoFileDialog.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceEngine.h>
#include <KoID.h>
#include <KoConfig.h>
#include <KoIcon.h>
#ifdef GHNS
#include <knewstuff3/downloaddialog.h>
#include <knewstuff3/uploaddialog.h>
#endif
#include <QDesktopServices>
#include <kcomponentdata.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include "kis_factory2.h"
+#include <kis_debug.h>
#include "ui_wdgcolorspaceselector.h"
struct KisColorSpaceSelector::Private {
Ui_WdgColorSpaceSelector* colorSpaceSelector;
QString knsrcFile;
bool profileValid;
QString defaultsuffix;
};
KisColorSpaceSelector::KisColorSpaceSelector(QWidget* parent) : QWidget(parent), m_advancedSelector(0), d(new Private)
{
setObjectName("KisColorSpaceSelector");
d->colorSpaceSelector = new Ui_WdgColorSpaceSelector;
d->colorSpaceSelector->setupUi(this);
d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible));
fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem());
d->colorSpaceSelector->bnInstallProfile->setIcon(themedIcon("document-open"));
d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") );
d->colorSpaceSelector->bnDownloadProfile->setIcon(themedIcon("download"));
d->colorSpaceSelector->bnDownloadProfile->setToolTip( i18n("Download Color Profile") );
d->colorSpaceSelector->bnDownloadProfile->setEnabled( true );
d->colorSpaceSelector->bnDownloadProfile->hide();
d->colorSpaceSelector->bnUploadProfile->setIcon(themedIcon("arrow-up"));
d->colorSpaceSelector->bnUploadProfile->setToolTip( i18n("Share Color Profile") );
d->colorSpaceSelector->bnUploadProfile->setEnabled( false );
d->colorSpaceSelector->bnUploadProfile->hide();
#ifdef GHNS
d->colorSpaceSelector->bnUploadProfile->show();
d->colorSpaceSelector->bnDownloadProfile->show();
#endif
connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)),
this, SLOT(fillCmbDepths(const KoID &)));
connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)),
this, SLOT(fillCmbProfiles()));
connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)),
this, SLOT(fillCmbProfiles()));
connect(d->colorSpaceSelector->cmbProfile, SIGNAL(activated(const QString &)),
this, SLOT(colorSpaceChanged()));
connect(d->colorSpaceSelector->cmbProfile, SIGNAL(currentIndexChanged(int)),
this, SLOT(buttonUpdate()));
connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile()));
connect(d->colorSpaceSelector->bnDownloadProfile, SIGNAL(clicked()), this, SLOT(downloadProfile()));
connect(d->colorSpaceSelector->bnUploadProfile, SIGNAL(clicked()), this, SLOT(uploadProfile()));
d->knsrcFile = "kritaiccprofiles.knsrc";
d->defaultsuffix = " "+i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)");
connect(d->colorSpaceSelector->bnAdvanced, SIGNAL(clicked()), this, SLOT(slotOpenAdvancedSelector()));
fillCmbProfiles();
}
KisColorSpaceSelector::~KisColorSpaceSelector()
{
delete d->colorSpaceSelector;
delete d;
}
void KisColorSpaceSelector::fillCmbProfiles()
{
QString s = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem());
d->colorSpaceSelector->cmbProfile->clear();
const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s);
if (csf == 0) return;
QList<const KoColorProfile *> profileList = KoColorSpaceRegistry::instance()->profilesFor(csf);
QStringList profileNames;
foreach(const KoColorProfile *profile, profileList) {
profileNames.append(profile->name());
}
qSort(profileNames);
foreach(QString stringName, profileNames) {
if (stringName==csf->defaultProfile()) {
d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName+d->defaultsuffix);
} else {
d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName);
}
}
d->colorSpaceSelector->cmbProfile->setCurrent(csf->defaultProfile()+d->defaultsuffix);
colorSpaceChanged();
}
void KisColorSpaceSelector::fillCmbDepths(const KoID& id)
{
KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem();
d->colorSpaceSelector->cmbColorDepth->clear();
QList<KoID> depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible);
d->colorSpaceSelector->cmbColorDepth->setIDList(depths);
if (depths.contains(activeDepth)) {
d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth);
}
}
const KoColorSpace* KisColorSpaceSelector::currentColorSpace()
{
QString profilenamestring = d->colorSpaceSelector->cmbProfile->itemHighlighted();
if (profilenamestring.contains(d->defaultsuffix)) {
profilenamestring.remove(d->defaultsuffix);
return KoColorSpaceRegistry::instance()->colorSpace(
d->colorSpaceSelector->cmbColorModels->currentItem().id(),
d->colorSpaceSelector->cmbColorDepth->currentItem().id(),
profilenamestring);
} else {
return KoColorSpaceRegistry::instance()->colorSpace(
d->colorSpaceSelector->cmbColorModels->currentItem().id(),
d->colorSpaceSelector->cmbColorDepth->currentItem().id(),
profilenamestring);
}
}
void KisColorSpaceSelector::setCurrentColorModel(const KoID& id)
{
d->colorSpaceSelector->cmbColorModels->setCurrent(id);
fillCmbDepths(id);
}
void KisColorSpaceSelector::setCurrentColorDepth(const KoID& id)
{
d->colorSpaceSelector->cmbColorDepth->setCurrent(id);
fillCmbProfiles();
}
void KisColorSpaceSelector::setCurrentProfile(const QString& name)
{
d->colorSpaceSelector->cmbProfile->setCurrent(name);
}
void KisColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace)
{
setCurrentColorModel(colorSpace->colorModelId());
setCurrentColorDepth(colorSpace->colorDepthId());
setCurrentProfile(colorSpace->profile()->name());
}
void KisColorSpaceSelector::colorSpaceChanged()
{
bool valid = d->colorSpaceSelector->cmbProfile->count() != 0;
d->profileValid = valid;
emit(selectionChanged(valid));
if(valid) {
emit colorSpaceChanged(currentColorSpace());
QString text = currentColorSpace()->profile()->name();
}
}
void KisColorSpaceSelector::installProfile()
{
QStringList mime;
mime << "*.icm" << "*.icc";
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC");
dialog.setCaption(i18n("Install Color Profiles"));
dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
dialog.setNameFilters(mime);
QStringList profileNames = dialog.urls();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
QString saveLocation = KGlobal::dirs()->saveLocation("icc_profiles");
foreach (const QString &profileName, profileNames) {
KUrl file(profileName);
if (!QFile::copy(profileName, saveLocation + file.fileName())) {
- kWarning() << "Could not install profile!";
+ dbgKrita << "Could not install profile!";
return;
}
iccEngine->addProfile(saveLocation + file.fileName());
}
fillCmbProfiles();
}
void KisColorSpaceSelector::downloadProfile()
{
#ifdef GHNS
KNS3::DownloadDialog dialog( "kritaiccprofiles.knsrc", this);
dialog.exec();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
foreach (const KNS3::Entry& e, dialog.changedEntries()) {
foreach(const QString &file, e.installedFiles()) {
QFileInfo fi(file);
iccEngine->addProfile( fi.absolutePath()+'/'+fi.fileName());
}
foreach(const QString &file, e.uninstalledFiles()) {
QFileInfo fi(file);
iccEngine->removeProfile( fi.absolutePath()+'/'+fi.fileName());
}
}
fillCmbProfiles();
#endif
}
void KisColorSpaceSelector::uploadProfile()
{
#ifdef GHNS
KNS3::UploadDialog dialog("kritaiccprofiles.knsrc", this);
const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(d->colorSpaceSelector->cmbProfile->currentText());
if(!profile) return;
dialog.setUploadFile(KUrl::fromLocalFile(profile->fileName()));
dialog.setUploadName(profile->name());
dialog.exec();
#endif
}
void KisColorSpaceSelector::buttonUpdate()
{
const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(d->colorSpaceSelector->cmbProfile->currentText());
if(!profile) return;
QFileInfo fileInfo(profile->fileName());
if(fileInfo.isWritable()) {
d->colorSpaceSelector->bnUploadProfile->setEnabled( true );
return;
}
d->colorSpaceSelector->bnUploadProfile->setEnabled( false );
}
void KisColorSpaceSelector::slotOpenAdvancedSelector()
{
if (!m_advancedSelector) {
m_advancedSelector = new KisAdvancedColorSpaceSelector(this, "Select a Colorspace");
m_advancedSelector->setModal(true);
m_advancedSelector->setCurrentColorSpace(currentColorSpace());
connect(m_advancedSelector, SIGNAL(selectionChanged(bool)), this, SLOT(slotProfileValid(bool)) );
}
QDialog::DialogCode result = (QDialog::DialogCode)m_advancedSelector->exec();
if (result) {
if (d->profileValid==true) {
setCurrentColorSpace(m_advancedSelector->currentColorSpace());
}
}
}
void KisColorSpaceSelector::slotProfileValid(bool valid)
{
d->profileValid = valid;
}
diff --git a/krita/ui/widgets/kis_custom_image_widget.cc b/krita/ui/widgets/kis_custom_image_widget.cc
index 038370693c4..ab19ff26838 100644
--- a/krita/ui/widgets/kis_custom_image_widget.cc
+++ b/krita/ui/widgets/kis_custom_image_widget.cc
@@ -1,473 +1,473 @@
/* This file is part of the Calligra project
* Copyright (C) 2005 Thomas Zander <zander@kde.org>
* Copyright (C) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "widgets/kis_custom_image_widget.h"
#include <QMimeData>
#include <QPushButton>
#include <QSlider>
#include <QComboBox>
#include <QRect>
#include <QApplication>
#include <QClipboard>
#include <QDesktopWidget>
#include <QFile>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QSpacerItem>
#include <QMessageBox>
#include <kcomponentdata.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <kis_debug.h>
#include <KoIcon.h>
#include <KoCompositeOp.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoID.h>
#include <KoColor.h>
#include <KoUnit.h>
#include <KoColorModelStandardIds.h>
#include <kis_fill_painter.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include "kis_config.h"
#include "KisPart.h"
#include "kis_clipboard.h"
#include "KisDocument.h"
#include "widgets/kis_cmb_idlist.h"
#include "widgets/squeezedcombobox.h"
KisCustomImageWidget::KisCustomImageWidget(QWidget* parent, qint32 defWidth, qint32 defHeight, double resolution, const QString& defColorModel, const QString& defColorDepth, const QString& defColorProfile, const QString& imageName)
: WdgNewImage(parent)
{
setObjectName("KisCustomImageWidget");
txtName->setText(imageName);
m_widthUnit = KoUnit(KoUnit::Pixel, resolution);
doubleWidth->setValue(defWidth);
doubleWidth->setDecimals(0);
m_width = m_widthUnit.fromUserValue(defWidth);
cmbWidthUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll));
cmbWidthUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll));
m_heightUnit = KoUnit(KoUnit::Pixel, resolution);
doubleHeight->setValue(defHeight);
doubleHeight->setDecimals(0);
m_height = m_heightUnit.fromUserValue(defHeight);
cmbHeightUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll));
cmbHeightUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll));
doubleResolution->setValue(72.0 * resolution);
doubleResolution->setDecimals(0);
imageGroupSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
grpClipboard->hide();
sliderOpacity->setRange(0, 100, 0);
sliderOpacity->setValue(100);
sliderOpacity->setSuffix("%");
connect(cmbPredefined, SIGNAL(activated(int)), SLOT(predefinedClicked(int)));
connect(doubleResolution, SIGNAL(valueChanged(double)),
this, SLOT(resolutionChanged(double)));
connect(cmbWidthUnit, SIGNAL(activated(int)),
this, SLOT(widthUnitChanged(int)));
connect(doubleWidth, SIGNAL(valueChanged(double)),
this, SLOT(widthChanged(double)));
connect(cmbHeightUnit, SIGNAL(activated(int)),
this, SLOT(heightUnitChanged(int)));
connect(doubleHeight, SIGNAL(valueChanged(double)),
this, SLOT(heightChanged(double)));
connect(createButton, SIGNAL(clicked()), this, SLOT(createImage()));
createButton->setDefault(true);
bnPortrait->setIcon(koIcon("portrait"));
connect(bnPortrait, SIGNAL(clicked()), SLOT(setPortrait()));
connect(bnLandscape, SIGNAL(clicked()), SLOT(setLandscape()));
bnLandscape->setIcon(koIcon("landscape"));
connect(doubleWidth, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape()));
connect(doubleHeight, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape()));
connect(bnSaveAsPredefined, SIGNAL(clicked()), this, SLOT(saveAsPredefined()));
colorSpaceSelector->setCurrentColorModel(KoID(defColorModel));
colorSpaceSelector->setCurrentColorDepth(KoID(defColorDepth));
colorSpaceSelector->setCurrentProfile(defColorProfile);
//connect(chkFromClipboard,SIGNAL(stateChanged(int)),this,SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), this, SLOT(clipboardDataChanged()));
connect(bnScreenSize, SIGNAL(clicked()), this, SLOT(screenSizeClicked()));
connect(colorSpaceSelector, SIGNAL(selectionChanged(bool)), createButton, SLOT(setEnabled(bool)));
KisConfig cfg;
intNumLayers->setValue(cfg.numDefaultLayers());
cmbColor->setColor(cfg.defaultBackgroundColor());
setBackgroundOpacity(cfg.defaultBackgroundOpacity());
KisConfig::BackgroundStyle bgStyle = cfg.defaultBackgroundStyle();
if (bgStyle == KisConfig::LAYER) {
radioBackgroundAsLayer->setChecked(true);
} else {
radioBackgroundAsProjection->setChecked(true);
}
fillPredefined();
switchPortraitLandscape();
}
void KisCustomImageWidget::showEvent(QShowEvent *)
{
fillPredefined();
this->createButton->setFocus();
}
KisCustomImageWidget::~KisCustomImageWidget()
{
qDeleteAll(m_predefined);
m_predefined.clear();
}
void KisCustomImageWidget::resolutionChanged(double res)
{
if (m_widthUnit.type() == KoUnit::Pixel) {
m_widthUnit.setFactor(res / 72.0);
m_width = m_widthUnit.fromUserValue(doubleWidth->value());
}
if (m_heightUnit.type() == KoUnit::Pixel) {
m_heightUnit.setFactor(res / 72.0);
m_height = m_heightUnit.fromUserValue(doubleHeight->value());
}
}
void KisCustomImageWidget::widthUnitChanged(int index)
{
doubleWidth->blockSignals(true);
m_widthUnit = KoUnit::fromListForUi(index, KoUnit::ListAll);
if (m_widthUnit.type() == KoUnit::Pixel) {
doubleWidth->setDecimals(0);
m_widthUnit.setFactor(doubleResolution->value() / 72.0);
} else {
doubleWidth->setDecimals(2);
}
doubleWidth->setValue(KoUnit::ptToUnit(m_width, m_widthUnit));
doubleWidth->blockSignals(false);
}
void KisCustomImageWidget::widthChanged(double value)
{
m_width = m_widthUnit.fromUserValue(value);
}
void KisCustomImageWidget::heightUnitChanged(int index)
{
doubleHeight->blockSignals(true);
m_heightUnit = KoUnit::fromListForUi(index, KoUnit::ListAll);
if (m_heightUnit.type() == KoUnit::Pixel) {
doubleHeight->setDecimals(0);
m_heightUnit.setFactor(doubleResolution->value() / 72.0);
} else {
doubleHeight->setDecimals(2);
}
doubleHeight->setValue(KoUnit::ptToUnit(m_height, m_heightUnit));
doubleHeight->blockSignals(false);
}
void KisCustomImageWidget::heightChanged(double value)
{
m_height = m_heightUnit.fromUserValue(value);
}
void KisCustomImageWidget::createImage()
{
KisDocument *doc = createNewImage();
if (doc) {
doc->setModified(false);
emit documentSelected(doc);
}
}
KisDocument* KisCustomImageWidget::createNewImage()
{
const KoColorSpace * cs = colorSpaceSelector->currentColorSpace();
if (cs->colorModelId() == RGBAColorModelID &&
cs->colorDepthId() == Integer8BitsColorDepthID) {
const KoColorProfile *profile = cs->profile();
if (profile->name().contains("linear") ||
profile->name().contains("scRGB") ||
profile->info().contains("linear") ||
profile->info().contains("scRGB")) {
int result =
QMessageBox::warning(this,
i18nc("@title:window", "Krita"),
i18n("Linear gamma RGB color spaces are not supposed to be used "
"in 8-bit integer modes. It is suggested to use 16-bit integer "
"or any floating point colorspace for linear profiles.\n\n"
"Press \"Continue\" to create a 8-bit integer linear RGB color space "
"or \"Cancel\" to return to the settings dialog."),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
if (result == QMessageBox::Cancel) {
- qDebug() << "Model RGB8" << "NOT SUPPORTED";
- qDebug() << ppVar(cs->name());
- qDebug() << ppVar(cs->profile()->name());
- qDebug() << ppVar(cs->profile()->info());
+ dbgKrita << "Model RGB8" << "NOT SUPPORTED";
+ dbgKrita << ppVar(cs->name());
+ dbgKrita << ppVar(cs->profile()->name());
+ dbgKrita << ppVar(cs->profile()->info());
return 0;
}
}
}
KisDocument *doc = static_cast<KisDocument*>(KisPart::instance()->createDocument());
qint32 width, height;
double resolution;
resolution = doubleResolution->value() / 72.0; // internal resolution is in pixels per pt
width = static_cast<qint32>(0.5 + KoUnit::ptToUnit(m_width, KoUnit(KoUnit::Pixel, resolution)));
height = static_cast<qint32>(0.5 + KoUnit::ptToUnit(m_height, KoUnit(KoUnit::Pixel, resolution)));
QColor qc = cmbColor->color();
qc.setAlpha(backgroundOpacity());
KoColor bgColor(qc, cs);
bool backgroundAsLayer = radioBackgroundAsLayer->isChecked();
doc->newImage(txtName->text(), width, height, cs, bgColor, backgroundAsLayer, intNumLayers->value(), txtDescription->toPlainText(), resolution);
KisConfig cfg;
cfg.setNumDefaultLayers(intNumLayers->value());
cfg.setDefaultBackgroundOpacity(backgroundOpacity());
cfg.setDefaultBackgroundColor(cmbColor->color());
cfg.setDefaultBackgroundStyle(backgroundAsLayer ? KisConfig::LAYER : KisConfig::PROJECTION);
return doc;
}
void KisCustomImageWidget::setNumberOfLayers(int layers)
{
intNumLayers->setValue(layers);
}
quint8 KisCustomImageWidget::backgroundOpacity() const
{
qint32 opacity = sliderOpacity->value();
if (!opacity)
return 0;
return (opacity * 255) / 100;
}
void KisCustomImageWidget::setBackgroundOpacity(quint8 value) {
sliderOpacity->setValue((value * 100) / 255);
}
void KisCustomImageWidget::clipboardDataChanged()
{
}
void KisCustomImageWidget::screenSizeClicked()
{
QSize sz = QApplication::desktop()->screenGeometry(this).size();
const int index = KoUnit(KoUnit::Pixel).indexInListForUi(KoUnit::ListAll);
cmbWidthUnit->setCurrentIndex(index);
cmbHeightUnit->setCurrentIndex(index);
widthUnitChanged(cmbWidthUnit->currentIndex());
heightUnitChanged(cmbHeightUnit->currentIndex());
doubleWidth->setValue(sz.width());
doubleHeight->setValue(sz.height());
}
void KisCustomImageWidget::fillPredefined()
{
cmbPredefined->clear();
cmbPredefined->addItem("");
QStringList definitions = KGlobal::dirs()->findAllResources("data", "krita/predefined_image_sizes/*", KStandardDirs::Recursive);
definitions.sort();
if (!definitions.empty()) {
foreach(const QString &definition, definitions) {
QFile f(definition);
f.open(QIODevice::ReadOnly);
if (f.exists()) {
QString xml = QString::fromUtf8(f.readAll());
KisPropertiesConfiguration *predefined = new KisPropertiesConfiguration;
predefined->fromXML(xml);
if (predefined->hasProperty("name")
&& predefined->hasProperty("width")
&& predefined->hasProperty("height")
&& predefined->hasProperty("resolution")
&& predefined->hasProperty("x-unit")
&& predefined->hasProperty("y-unit")) {
m_predefined << predefined;
cmbPredefined->addItem(predefined->getString("name"));
}
}
}
}
cmbPredefined->setCurrentIndex(0);
}
void KisCustomImageWidget::predefinedClicked(int index)
{
if (index < 1 || index > m_predefined.size()) return;
KisPropertiesConfiguration *predefined = m_predefined[index - 1];
txtPredefinedName->setText(predefined->getString("name"));
doubleResolution->setValue(predefined->getDouble("resolution"));
cmbWidthUnit->setCurrentIndex(predefined->getInt("x-unit"));
cmbHeightUnit->setCurrentIndex(predefined->getInt("y-unit"));
widthUnitChanged(cmbWidthUnit->currentIndex());
heightUnitChanged(cmbHeightUnit->currentIndex());
doubleWidth->setValue(predefined->getDouble("width"));
doubleHeight->setValue(predefined->getDouble("height"));
}
void KisCustomImageWidget::saveAsPredefined()
{
QString fileName = txtPredefinedName->text();
if (fileName.isEmpty()) {
return;
}
QString saveLocation = KGlobal::dirs()->saveLocation("data");
QDir d;
d.mkpath(saveLocation + "krita/predefined_image_sizes/");
QFile f(saveLocation + "krita/predefined_image_sizes/" + fileName.replace(' ', '_').replace('(', '_').replace(')', '_') + ".predefinedimage");
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
KisPropertiesConfiguration *predefined = new KisPropertiesConfiguration();
predefined->setProperty("name", txtPredefinedName->text());
predefined->setProperty("width", doubleWidth->value());
predefined->setProperty("height", doubleHeight->value());
predefined->setProperty("resolution", doubleResolution->value());
predefined->setProperty("x-unit", cmbWidthUnit->currentIndex());
predefined->setProperty("y-unit", cmbHeightUnit->currentIndex());
QString xml = predefined->toXML();
f.write(xml.toUtf8());
f.flush();
f.close();
int i = 0;
bool found = false;
foreach(KisPropertiesConfiguration *pr, m_predefined) {
if (pr->getString("name") == txtPredefinedName->text()) {
found = true;
break;
}
++i;
}
if (found) {
m_predefined[i] = predefined;
}
else {
m_predefined.append(predefined);
cmbPredefined->addItem(txtPredefinedName->text());
}
}
void KisCustomImageWidget::setLandscape()
{
if (doubleWidth->value() < doubleHeight->value()) {
switchWidthHeight();
}
}
void KisCustomImageWidget::setPortrait()
{
if (doubleWidth->value() > doubleHeight->value()) {
switchWidthHeight();
}
}
void KisCustomImageWidget::switchWidthHeight()
{
double width = doubleWidth->value();
double height = doubleHeight->value();
doubleHeight->blockSignals(true);
doubleWidth->blockSignals(true);
cmbWidthUnit->blockSignals(true);
cmbHeightUnit->blockSignals(true);
doubleWidth->setValue(height);
doubleHeight->setValue(width);
cmbWidthUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll));
cmbHeightUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll));
doubleHeight->blockSignals(false);
doubleWidth->blockSignals(false);
cmbWidthUnit->blockSignals(false);
cmbHeightUnit->blockSignals(false);
switchPortraitLandscape();
widthChanged(doubleWidth->value());
heightChanged(doubleHeight->value());
}
void KisCustomImageWidget::switchPortraitLandscape()
{
if(doubleWidth->value() > doubleHeight->value())
bnLandscape->setChecked(true);
else
bnPortrait->setChecked(true);
}
diff --git a/krita/ui/widgets/kis_filter_selector_widget.h b/krita/ui/widgets/kis_filter_selector_widget.h
index 2652f010ce9..abe5184ba95 100644
--- a/krita/ui/widgets/kis_filter_selector_widget.h
+++ b/krita/ui/widgets/kis_filter_selector_widget.h
@@ -1,140 +1,140 @@
/*
* Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
*
* 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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_FILTER_SELECTOR_WIDGET_H_
#define _KIS_FILTER_SELECTOR_WIDGET_H_
#include <QWidget>
#include <QTreeView>
#include <QHeaderView>
-#include <QDebug>
+#include <kis_debug.h>
#include <QResizeEvent>
#include <QSize>
#include <kis_types.h>
class QModelIndex;
class KisFilterConfiguration;
class KisViewManager;
class QAbstractItemModel;
class QHideEvent;
class QShowEvent;
/**
* XXX
*/
class KisFilterSelectorWidget : public QWidget
{
Q_OBJECT
public:
KisFilterSelectorWidget(QWidget* parent);
~KisFilterSelectorWidget();
void setFilter(KisFilterSP f);
void setView(KisViewManager *view);
void setPaintDevice(bool showAll, KisPaintDeviceSP);
KisFilterConfiguration* configuration();
bool isFilterGalleryVisible() const;
KisFilterSP currentFilter() const;
public Q_SLOTS:
void setVisible(bool visible);
void showFilterGallery(bool visible);
protected Q_SLOTS:
void slotBookmarkedFilterConfigurationSelected(int);
void setFilterIndex(const QModelIndex&);
void editConfigurations();
void update();
Q_SIGNALS:
void configurationChanged();
void sigFilterGalleryToggled(bool visible);
void sigSizeChanged();
private:
struct Private;
Private* const d;
};
class KisFilterTree: public QTreeView
{
Q_OBJECT
public:
KisFilterTree(QWidget *parent) : QTreeView(parent) {
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(update_scroll_area(QModelIndex)));
connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(update_scroll_area(QModelIndex)));
}
void setFilterModel(QAbstractItemModel * model);
void activateFilter(QModelIndex idx);
QSize minimumSizeHint() const
{
return QSize(200, QTreeView::sizeHint().height());
}
QSize sizeHint() const
{
return QSize(header()->width(), QTreeView::sizeHint().height());
}
void setModel(QAbstractItemModel *model)
{
QTreeView::setModel(model);
header()->setResizeMode(0, QHeaderView::ResizeToContents);
}
protected:
void resizeEvent(QResizeEvent *event)
{
if (event->size().width() > 10) {
setModel(m_model);
}
else {
setModel(0);
}
QTreeView::resizeEvent(event);
}
void showEvent(QShowEvent * event)
{
setModel(m_model);
QTreeView::showEvent(event);
}
void hideEvent(QHideEvent * event)
{
setModel(0);
QTreeView::hideEvent(event);
}
private Q_SLOTS:
void update_scroll_area(const QModelIndex& i)
{
resizeColumnToContents(i.column());
}
private:
QAbstractItemModel *m_model;
};
#endif
diff --git a/krita/ui/widgets/kis_gradient_slider.cpp b/krita/ui/widgets/kis_gradient_slider.cpp
index a1f46289c77..360ec5be5d8 100644
--- a/krita/ui/widgets/kis_gradient_slider.cpp
+++ b/krita/ui/widgets/kis_gradient_slider.cpp
@@ -1,361 +1,361 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Frederic Coiffier <fcoiffie@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Local includes.
#include "kis_gradient_slider.h"
// C++ includes.
#include <cmath>
#include <cstdlib>
// Qt includes.
-#include <QDebug>
+#include <kis_debug.h>
#include <QtGlobal>
#include <QPainter>
#include <QPoint>
#include <QPen>
#include <QMouseEvent>
#include <QBrush>
#include <QLinearGradient>
#define MARGIN 5
#define HANDLE_SIZE 10
KisGradientSlider::KisGradientSlider(QWidget *parent)
: QWidget(parent)
, m_leftmost(0)
, m_rightmost(0)
, m_scalingFactor(0)
, m_blackCursor(0)
, m_whiteCursor(0)
, m_gammaCursor(0)
, m_black(0)
, m_white(255)
, m_gamma(1.0)
, m_gammaEnabled(false)
, m_feedback(false)
{
m_grabCursor = None;
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
}
KisGradientSlider::~KisGradientSlider()
{
}
int KisGradientSlider::black() const
{
return m_black;
}
int KisGradientSlider::white() const
{
return m_white;
}
void KisGradientSlider::paintEvent(QPaintEvent *e)
{
QWidget::paintEvent(e);
int x, y;
int wWidth = width() - (2 * MARGIN);
int wHeight = height();
const int gradientHeight = qRound((double)wHeight / 7.0 * 2);
QPainter p1(this);
p1.fillRect(rect(), palette().background());
p1.setPen(Qt::black);
p1.drawRect(MARGIN, MARGIN, wWidth, height() - 2 * MARGIN - HANDLE_SIZE);
// Draw first gradient
QLinearGradient grayGradient(MARGIN, 0, wWidth, gradientHeight);
grayGradient.setColorAt(0, Qt::black);
grayGradient.setColorAt(1, Qt::white);
p1.fillRect(MARGIN, 0, wWidth, gradientHeight, QBrush(grayGradient));
// Draw second gradient
y = gradientHeight;
p1.fillRect(MARGIN, y, wWidth, gradientHeight, Qt::white);
if (m_blackCursor > 0) {
p1.fillRect(MARGIN, y, m_blackCursor, gradientHeight, Qt::black);
}
for (x = (int)m_blackCursor + MARGIN; x < (int)m_whiteCursor - MARGIN; ++x) {
double inten = (double)(x - (m_blackCursor + MARGIN)) / (double)((m_whiteCursor - MARGIN) - (m_blackCursor + MARGIN));
inten = pow(inten, (1.0 / m_gamma));
int gray = (int)(255 * inten);
p1.setPen(QColor(gray, gray, gray));
p1.drawLine(x, y, x, y + gradientHeight - 1);
}
// Draw cursors
y += gradientHeight;
QPoint a[3];
p1.setPen(Qt::darkGray);
p1.setRenderHint(QPainter::Antialiasing, true);
const int cursorHalfBase = (int)(gradientHeight / 1.5);
a[0] = QPoint(m_blackCursor + MARGIN, y);
a[1] = QPoint(m_blackCursor + MARGIN + cursorHalfBase, wHeight - 1);
a[2] = QPoint(m_blackCursor + MARGIN - cursorHalfBase, wHeight - 1);
p1.setBrush(Qt::black);
p1.drawPolygon(a, 3);
p1.setPen(Qt::black);
if (m_gammaEnabled) {
a[0] = QPoint(m_gammaCursor, y);
a[1] = QPoint(m_gammaCursor + cursorHalfBase, wHeight - 1);
a[2] = QPoint(m_gammaCursor - cursorHalfBase, wHeight - 1);
p1.setBrush(Qt::gray);
p1.drawPolygon(a, 3);
}
a[0] = QPoint(m_whiteCursor - MARGIN, y);
a[1] = QPoint(m_whiteCursor - MARGIN + cursorHalfBase, wHeight - 1);
a[2] = QPoint(m_whiteCursor - MARGIN - cursorHalfBase, wHeight - 1);
p1.setBrush(Qt::white);
p1.drawPolygon(a, 3);
}
void KisGradientSlider::resizeEvent(QResizeEvent *)
{
m_scalingFactor = (double)(width() - MARGIN) / 255;
calculateCursorPositions();
update();
}
void KisGradientSlider::mousePressEvent(QMouseEvent * e)
{
eCursor closest_cursor;
int distance;
if (e->button() != Qt::LeftButton)
return;
unsigned int x = e->pos().x();
int xPlusMargin = x + MARGIN;
distance = width() + 1; // just a big number
if (abs((int)(xPlusMargin - m_blackCursor)) < distance) {
distance = abs((int)(xPlusMargin - m_blackCursor));
closest_cursor = BlackCursor;
}
if (abs((int)(xPlusMargin - m_whiteCursor)) < distance) {
distance = abs((int)(xPlusMargin - m_whiteCursor));
closest_cursor = WhiteCursor;
}
if (m_gammaEnabled) {
int gammaDistance = (int)xPlusMargin - m_gammaCursor;
if (abs(gammaDistance) < distance) {
distance = abs((int)xPlusMargin - m_gammaCursor);
closest_cursor = GammaCursor;
} else if (abs(gammaDistance) == distance) {
if ((closest_cursor == BlackCursor) && (gammaDistance > 0)) {
distance = abs(gammaDistance);
closest_cursor = GammaCursor;
} else if ((closest_cursor == WhiteCursor) && (gammaDistance < 0)) {
distance = abs(gammaDistance);
closest_cursor = GammaCursor;
}
}
}
if (distance > 20) {
m_grabCursor = None;
return;
}
// Determine cursor values and the leftmost and rightmost points.
switch (closest_cursor) {
case BlackCursor:
m_blackCursor = x - MARGIN;
m_grabCursor = closest_cursor;
m_leftmost = 0;
m_rightmost = m_whiteCursor - ((MARGIN + 1) * m_scalingFactor);
if (m_gammaEnabled)
m_gammaCursor = calculateGammaCursor();
break;
case WhiteCursor:
m_whiteCursor = x + MARGIN;
m_grabCursor = closest_cursor;
m_leftmost = m_blackCursor + (MARGIN * m_scalingFactor);
m_rightmost = width() - MARGIN ;
if (m_gammaEnabled)
m_gammaCursor = calculateGammaCursor();
break;
case GammaCursor:
m_gammaCursor = x;
m_grabCursor = closest_cursor;
m_leftmost = m_blackCursor + (MARGIN * m_scalingFactor);
m_rightmost = m_whiteCursor - (MARGIN * m_scalingFactor);
{
double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
double mid = (double)m_blackCursor + delta + MARGIN;
double tmp = (x - mid) / delta;
m_gamma = 1.0 / pow(10, tmp);
}
break;
default:
break;
}
update();
}
void KisGradientSlider::mouseReleaseEvent(QMouseEvent * e)
{
if (e->button() != Qt::LeftButton)
return;
update();
switch (m_grabCursor) {
case BlackCursor:
m_black = qRound( m_blackCursor / m_scalingFactor);
m_feedback = true;
emit sigModifiedBlack(m_black);
break;
case WhiteCursor:
m_white = qRound( (m_whiteCursor - MARGIN) / m_scalingFactor);
m_feedback = true;
emit sigModifiedWhite(m_white);
break;
case GammaCursor:
emit sigModifiedGamma(m_gamma);
break;
default:
break;
}
m_grabCursor = None;
m_feedback = false;
}
void KisGradientSlider::mouseMoveEvent(QMouseEvent * e)
{
int x = e->pos().x();
if (m_grabCursor != None) { // Else, drag the selected point
if (x + MARGIN <= m_leftmost)
x = m_leftmost;
if (x >= m_rightmost)
x = m_rightmost;
switch (m_grabCursor) {
case BlackCursor:
if (m_blackCursor != x) {
m_blackCursor = x;
if (m_gammaEnabled) {
m_gammaCursor = calculateGammaCursor();
}
}
break;
case WhiteCursor:
if (m_whiteCursor != x) {
m_whiteCursor = x + MARGIN;
if (m_gammaEnabled) {
m_gammaCursor = calculateGammaCursor();
}
}
break;
case GammaCursor:
if (m_gammaCursor != x) {
m_gammaCursor = x;
double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
double mid = (double)m_blackCursor + delta;
double tmp = (x - mid) / delta;
m_gamma = 1.0 / pow(10, tmp);
}
break;
default:
break;
}
}
update();
}
void KisGradientSlider::calculateCursorPositions()
{
m_blackCursor = qRound(m_black * m_scalingFactor);
m_whiteCursor = qRound(m_white * m_scalingFactor + MARGIN);
m_gammaCursor = calculateGammaCursor();
}
unsigned int KisGradientSlider::calculateGammaCursor()
{
double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
double mid = (double)m_blackCursor + delta;
double tmp = log10(1.0 / m_gamma);
return (unsigned int)qRound(mid + delta * tmp);
}
void KisGradientSlider::enableGamma(bool b)
{
m_gammaEnabled = b;
update();
}
double KisGradientSlider::getGamma(void)
{
return m_gamma;
}
void KisGradientSlider::slotModifyBlack(int v)
{
if (v >= 0 && v <= (int)m_white && !m_feedback) {
m_black = v;
m_blackCursor = qRound(m_black * m_scalingFactor);
m_gammaCursor = calculateGammaCursor();
update();
}
}
void KisGradientSlider::slotModifyWhite(int v)
{
if (v >= (int)m_black && v <= width() && !m_feedback) {
m_white = v;
m_whiteCursor = qRound(m_white * m_scalingFactor + MARGIN);
m_gammaCursor = calculateGammaCursor();
update();
}
}
void KisGradientSlider::slotModifyGamma(double v)
{
if (m_gamma != v) {
emit sigModifiedGamma(v);
}
m_gamma = v;
m_gammaCursor = calculateGammaCursor();
update();
}
diff --git a/krita/ui/widgets/kis_progress_widget.cpp b/krita/ui/widgets/kis_progress_widget.cpp
index 05ddd064f28..bc656473393 100644
--- a/krita/ui/widgets/kis_progress_widget.cpp
+++ b/krita/ui/widgets/kis_progress_widget.cpp
@@ -1,95 +1,95 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* 2004 Adrian Page <adrian@pagenet.plus.com>
* 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_progress_widget.h"
-#include <QDebug>
+#include <kis_debug.h>
#include <QToolButton>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <KoIcon.h>
#include <KoProgressUpdater.h>
#include <KoProgressBar.h>
#include <kis_progress_updater.h>
KisProgressWidget::KisProgressWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
m_cancelButton = new QToolButton(this);
m_cancelButton->setIcon(themedIcon("process-stop"));
QSizePolicy sizePolicy = m_cancelButton->sizePolicy();
sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
m_cancelButton->setSizePolicy(sizePolicy);
connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));
m_progressBar = new KoProgressBar(this);
connect(m_progressBar, SIGNAL(valueChanged(int)), SLOT(correctVisibility(int)));
layout->addWidget(m_progressBar);
layout->addWidget(m_cancelButton);
layout->setContentsMargins(0, 0, 0, 0);
m_progressBar->setVisible(false);
m_cancelButton->setVisible(false);
setMaximumWidth(225);
setMinimumWidth(225);
}
KisProgressWidget::~KisProgressWidget()
{
}
KoProgressProxy* KisProgressWidget::progressProxy()
{
return m_progressBar;
}
void KisProgressWidget::cancel()
{
foreach(KoProgressUpdater* updater, m_activeUpdaters) {
updater->cancel();
}
emit sigCancellationRequested();
}
void KisProgressWidget::correctVisibility(int progressValue)
{
bool visibility = progressValue >= m_progressBar->minimum() &&
progressValue < m_progressBar->maximum();
m_progressBar->setVisible(visibility);
m_cancelButton->setVisible(visibility);
}
void KisProgressWidget::detachUpdater(KoProgressUpdater* updater)
{
m_activeUpdaters.removeOne(updater);
}
void KisProgressWidget::attachUpdater(KoProgressUpdater* updater)
{
m_activeUpdaters << updater;
}
diff --git a/krita/ui/widgets/squeezedcombobox.cpp b/krita/ui/widgets/squeezedcombobox.cpp
index cffd231b4f2..c5185785ced 100644
--- a/krita/ui/widgets/squeezedcombobox.cpp
+++ b/krita/ui/widgets/squeezedcombobox.cpp
@@ -1,148 +1,148 @@
/* ============================================================
* Author: Tom Albers <tomalbers@kde.nl>
* Date : 2005-01-01
* Description :
*
* Copyright 2005 by Tom Albers <tomalbers@kde.nl>
*
* 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 "widgets/squeezedcombobox.h"
/** @file squeezedcombobox.cpp */
// Qt includes.
#include <QComboBox>
#include <QPair>
#include <QTimer>
#include <QStyle>
#include <QApplication>
#include <QResizeEvent>
-#include <kdebug.h>
+#include <kis_debug.h>
SqueezedComboBox::SqueezedComboBox(QWidget *parent, const char *name)
: QComboBox(parent)
{
setObjectName(name);
setMinimumWidth(100);
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(m_timer, SIGNAL(timeout()),
SLOT(slotTimeOut()));
}
SqueezedComboBox::~SqueezedComboBox()
{
delete m_timer;
}
bool SqueezedComboBox::contains(const QString& _text) const
{
if (_text.isEmpty())
return false;
for (QMap<int, QString>::const_iterator it = m_originalItems.begin() ; it != m_originalItems.end();
++it) {
if (it.value() == _text) {
return true;
}
}
return false;
}
qint32 SqueezedComboBox::findOriginalText(const QString& text) const
{
for(int i = 0; i < m_originalItems.size(); i++) {
if(m_originalItems.value(i) == text) {
return i;
}
}
return -1;
}
QSize SqueezedComboBox::sizeHint() const
{
ensurePolished();
QFontMetrics fm = fontMetrics();
int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18;
int maxH = qMax(fm.lineSpacing(), 14) + 2;
QStyleOptionComboBox options;
options.initFrom(this);
return style()->sizeFromContents(QStyle::CT_ComboBox, &options,
QSize(maxW, maxH), this).expandedTo(QApplication::globalStrut());
}
void SqueezedComboBox::insertSqueezedItem(const QString& newItem, int index, QVariant userData)
{
m_originalItems[index] = newItem;
QComboBox::insertItem(index, squeezeText(newItem), userData);
}
void SqueezedComboBox::addSqueezedItem(const QString& newItem, QVariant userData)
{
insertSqueezedItem(newItem, count(), userData);
}
void SqueezedComboBox::setCurrent(const QString& itemText)
{
qint32 itemIndex = findOriginalText(itemText);
if (itemIndex >= 0) {
setCurrentIndex(itemIndex);
}
}
void SqueezedComboBox::resizeEvent(QResizeEvent *)
{
m_timer->start(200);
}
void SqueezedComboBox::slotTimeOut()
{
for (QMap<int, QString>::iterator it = m_originalItems.begin() ; it != m_originalItems.end();
++it) {
setItemText(it.key(), squeezeText(it.value()));
}
}
QString SqueezedComboBox::squeezeText(const QString& original)
{
// not the complete widgetSize is usable. Need to compensate for that.
int widgetSize = width() - 30;
QFontMetrics fm(fontMetrics());
// If we can fit the full text, return that.
if (fm.width(original) < widgetSize)
return(original);
// We need to squeeze.
QString sqItem = original; // prevent empty return value;
widgetSize = widgetSize - fm.width("...");
for (int i = 0 ; i != original.length(); ++i) {
if ((int)fm.width(original.right(i)) > widgetSize) {
sqItem = QString("..." + original.right(--i));
break;
}
}
return sqItem;
}
QString SqueezedComboBox::itemHighlighted()
{
int curItem = currentIndex();
return m_originalItems[curItem];
}
diff --git a/libs/flake/KoImageData_p.cpp b/libs/flake/KoImageData_p.cpp
index ba4d43c8d62..a586bfa2280 100644
--- a/libs/flake/KoImageData_p.cpp
+++ b/libs/flake/KoImageData_p.cpp
@@ -1,167 +1,167 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2008 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoImageData_p.h"
#include "KoImageCollection.h"
#include <QApplication>
#include <QTemporaryFile>
#include <QImageWriter>
#include <QCryptographicHash>
#include <QFileInfo>
#include <kdebug.h>
#include <QBuffer>
KoImageDataPrivate::KoImageDataPrivate(KoImageData *q)
: collection(0),
errorCode(KoImageData::Success),
key(0),
refCount(0),
dataStoreState(StateEmpty),
temporaryFile(0)
{
cleanCacheTimer.setSingleShot(true);
cleanCacheTimer.setInterval(1000);
QObject::connect(&cleanCacheTimer, SIGNAL(timeout()), q, SLOT(cleanupImageCache()));
}
KoImageDataPrivate::~KoImageDataPrivate()
{
if (collection)
collection->removeOnKey(key);
delete temporaryFile;
}
// called from the collection
bool KoImageDataPrivate::saveData(QIODevice &device)
{
// if we have a temp file save that to the store. This is needed as to not loose data when
// saving lossy formats. Also wrinting out gif is not supported by qt so saving temp file
// also fixes the problem that gif images are empty after saving-
if (temporaryFile) {
if (!temporaryFile->open()) {
kWarning(30006) << "Read file from temporary store failed";
return false;
}
char buf[4096];
while (true) {
temporaryFile->waitForReadyRead(-1);
qint64 bytes = temporaryFile->read(buf, sizeof(buf));
if (bytes <= 0)
break; // done!
do {
qint64 nWritten = device.write(buf, bytes);
if (nWritten == -1) {
temporaryFile->close();
return false;
}
bytes -= nWritten;
} while (bytes > 0);
}
temporaryFile->close();
return true;
}
switch (dataStoreState) {
case KoImageDataPrivate::StateEmpty:
return false;
case KoImageDataPrivate::StateNotLoaded:
// we should not reach this state as above this will already be saved.
Q_ASSERT(temporaryFile);
return true;
case KoImageDataPrivate::StateImageLoaded:
case KoImageDataPrivate::StateImageOnly: {
// save image
QBuffer buffer;
QImageWriter writer(&buffer, suffix.toLatin1());
bool result = writer.write(image);
device.write(buffer.data(), buffer.size());
return result;
}
}
return false;
}
void KoImageDataPrivate::setSuffix(const QString &name)
{
QRegExp rx("\\.([^/]+$)"); // TODO does this work on windows or do we have to use \ instead of / for a path separator?
if (rx.indexIn(name) != -1) {
suffix = rx.cap(1);
}
}
void KoImageDataPrivate::copyToTemporary(QIODevice &device)
{
delete temporaryFile;
- temporaryFile = new QTemporaryFile(QLatin1String("KoImageData/") + qAppName() + QLatin1String("_XXXXXX"));
+ temporaryFile = new QTemporaryFile(QDir::tempPath() + "/" + qAppName() + QLatin1String("_XXXXXX"));
if (!temporaryFile->open()) {
kWarning(30006) << "open temporary file for writing failed";
errorCode = KoImageData::StorageFailed;
return;
}
QCryptographicHash md5(QCryptographicHash::Md5);
char buf[8096];
while (true) {
device.waitForReadyRead(-1);
qint64 bytes = device.read(buf, sizeof(buf));
if (bytes <= 0)
break; // done!
md5.addData(buf, bytes);
do {
bytes -= temporaryFile->write(buf, bytes);
} while (bytes > 0);
}
key = KoImageDataPrivate::generateKey(md5.result());
temporaryFile->close();
//QFileInfo fi(*temporaryFile);
dataStoreState = StateNotLoaded;
}
void KoImageDataPrivate::cleanupImageCache()
{
if (dataStoreState == KoImageDataPrivate::StateImageLoaded) {
image = QImage();
dataStoreState = KoImageDataPrivate::StateNotLoaded;
}
}
void KoImageDataPrivate::clear()
{
errorCode = KoImageData::Success;
dataStoreState = StateEmpty;
imageLocation.clear();
imageSize = QSizeF();
key = 0;
image = QImage();
pixmap = QPixmap();
}
qint64 KoImageDataPrivate::generateKey(const QByteArray &bytes)
{
qint64 answer = 1;
const int max = qMin(8, bytes.count());
for (int x = 0; x < max; ++x)
answer += bytes[x] << (8 * x);
return answer;
}
diff --git a/libs/flake/KoImageData_p.h b/libs/flake/KoImageData_p.h
index decada45097..38502e52cb6 100644
--- a/libs/flake/KoImageData_p.h
+++ b/libs/flake/KoImageData_p.h
@@ -1,90 +1,91 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2008 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOIMAGEDATA_P_H
#define KOIMAGEDATA_P_H
#include <QUrl>
#include <QByteArray>
#include <QImage>
#include <QPixmap>
#include <QTimer>
+#include <QDir>
#include "KoImageData.h"
class KoImageCollection;
class QTemporaryFile;
class KoImageDataPrivate
{
public:
explicit KoImageDataPrivate(KoImageData *q);
virtual ~KoImageDataPrivate();
/**
* Save the image data to the param device.
* The full file is saved.
* @param device the device that is used to get the data from.
* @return returns true if load was successful.
*/
bool saveData(QIODevice &device);
/// store the suffix based on the full filename.
void setSuffix(const QString &fileName);
/// take the data from \a device and store it in the temporaryFile
void copyToTemporary(QIODevice &device);
/// clean the image cache.
void cleanupImageCache();
void clear();
static qint64 generateKey(const QByteArray &bytes);
enum DataStoreState {
StateEmpty, ///< No image data, either as url or as QImage
StateNotLoaded, ///< Image data is set as Url
StateImageLoaded,///< Image data is loaded from Url, so both are present.
StateImageOnly ///< Image data is stored in a QImage. There is no external storage.
};
KoImageCollection *collection;
KoImageData::ErrorCode errorCode;
QSizeF imageSize;
qint64 key;
QString suffix; // the suffix of the picture e.g. png TODO use a QByteArray ?
QTimer cleanCacheTimer;
QAtomicInt refCount;
// Image data store.
DataStoreState dataStoreState;
QUrl imageLocation;
QImage image;
/// screen optimized cached version.
QPixmap pixmap;
QTemporaryFile *temporaryFile;
};
#endif /* KOIMAGEDATA_P_H */
diff --git a/libs/flake/KoShapeGroup.cpp b/libs/flake/KoShapeGroup.cpp
index 126bd3795c8..c60c1ae95a9 100644
--- a/libs/flake/KoShapeGroup.cpp
+++ b/libs/flake/KoShapeGroup.cpp
@@ -1,248 +1,250 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoShapeGroup.h"
#include "KoShapeContainerModel.h"
#include "KoShapeContainer_p.h"
#include "KoShapeLayer.h"
#include "SimpleShapeContainerModel.h"
#include "KoShapeSavingContext.h"
#include "KoShapeLoadingContext.h"
#include "KoXmlWriter.h"
#include "KoXmlReader.h"
#include "KoShapeRegistry.h"
#include "KoShapeStrokeModel.h"
#include "KoShapeShadow.h"
#include "KoInsets.h"
+#include <kdebug.h>
+
#include <QPainter>
class ShapeGroupContainerModel : public SimpleShapeContainerModel
{
public:
ShapeGroupContainerModel(KoShapeGroup *group) : m_group(group) {}
~ShapeGroupContainerModel() {}
virtual void add(KoShape *child)
{
SimpleShapeContainerModel::add(child);
m_group->invalidateSizeCache();
}
virtual void remove(KoShape *child)
{
SimpleShapeContainerModel::remove(child);
m_group->invalidateSizeCache();
}
virtual void childChanged(KoShape *shape, KoShape::ChangeType type)
{
SimpleShapeContainerModel::childChanged(shape, type);
//kDebug(30006) << type;
switch (type) {
case KoShape::PositionChanged:
case KoShape::RotationChanged:
case KoShape::ScaleChanged:
case KoShape::ShearChanged:
case KoShape::SizeChanged:
case KoShape::GenericMatrixChange:
case KoShape::ParameterChanged:
case KoShape::ClipPathChanged :
m_group->invalidateSizeCache();
break;
default:
break;
}
}
private: // members
KoShapeGroup * m_group;
};
class KoShapeGroupPrivate : public KoShapeContainerPrivate
{
public:
KoShapeGroupPrivate(KoShapeGroup *q)
: KoShapeContainerPrivate(q)
{
model = new ShapeGroupContainerModel(q);
}
~KoShapeGroupPrivate()
{
}
mutable bool sizeCached;
};
KoShapeGroup::KoShapeGroup()
: KoShapeContainer(*(new KoShapeGroupPrivate(this)))
{
setSize(QSizeF(0, 0));
}
KoShapeGroup::~KoShapeGroup()
{
}
void KoShapeGroup::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &)
{
Q_UNUSED(painter);
Q_UNUSED(converter);
}
bool KoShapeGroup::hitTest(const QPointF &position) const
{
Q_UNUSED(position);
return false;
}
QSizeF KoShapeGroup::size() const
{
Q_D(const KoShapeGroup);
//kDebug(30006) << "size" << d->size;
if (!d->sizeCached) {
QRectF bound;
foreach(KoShape *shape, shapes()) {
if (bound.isEmpty())
bound = shape->transformation().mapRect(shape->outlineRect());
else
bound |= shape->transformation().mapRect(shape->outlineRect());
}
d->size = bound.size();
d->sizeCached = true;
kDebug(30006) << "recalculated size" << d->size;
}
return d->size;
}
QRectF KoShapeGroup::boundingRect() const
{
bool first = true;
QRectF groupBound;
foreach(KoShape* shape, shapes()) {
if (first) {
groupBound = shape->boundingRect();
first = false;
} else {
groupBound = groupBound.united(shape->boundingRect());
}
}
if (shadow()) {
KoInsets insets;
shadow()->insets(insets);
groupBound.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
return groupBound;
}
void KoShapeGroup::saveOdf(KoShapeSavingContext & context) const
{
context.xmlWriter().startElement("draw:g");
saveOdfAttributes(context, (OdfMandatories ^ (OdfLayer | OdfZIndex)) | OdfAdditionalAttributes);
context.xmlWriter().addAttributePt("svg:y", position().y());
QList<KoShape*> shapes = this->shapes();
qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
foreach(KoShape* shape, shapes) {
shape->saveOdf(context);
}
saveOdfCommonChildElements(context);
context.xmlWriter().endElement();
}
bool KoShapeGroup::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
{
Q_D(KoShapeGroup);
loadOdfAttributes(element, context, OdfMandatories | OdfStyle | OdfAdditionalAttributes | OdfCommonChildElements);
KoXmlElement child;
QMap<KoShapeLayer*, int> usedLayers;
forEachElement(child, element) {
KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, context);
if (shape) {
KoShapeLayer *layer = dynamic_cast<KoShapeLayer*>(shape->parent());
if (layer) {
usedLayers[layer]++;
}
addShape(shape);
}
}
KoShapeLayer *parent = 0;
int maxUseCount = 0;
// find most used layer and use this as parent for the group
for (QMap<KoShapeLayer*, int>::const_iterator it(usedLayers.constBegin()); it != usedLayers.constEnd(); ++it) {
if (it.value() > maxUseCount) {
maxUseCount = it.value();
parent = it.key();
}
}
setParent(parent);
QRectF bound;
bool boundInitialized = false;
foreach(KoShape * shape, shapes()) {
if (! boundInitialized) {
bound = shape->boundingRect();
boundInitialized = true;
} else
bound = bound.united(shape->boundingRect());
}
setSize(bound.size());
d->sizeCached = true;
setPosition(bound.topLeft());
foreach(KoShape * shape, shapes())
shape->setAbsolutePosition(shape->absolutePosition() - bound.topLeft());
return true;
}
void KoShapeGroup::shapeChanged(ChangeType type, KoShape *shape)
{
Q_UNUSED(shape);
KoShapeContainer::shapeChanged(type, shape);
switch (type) {
case KoShape::StrokeChanged:
{
KoShapeStrokeModel *str = stroke();
if (str) {
if (str->deref())
delete str;
setStroke(0);
}
break;
}
default:
break;
}
}
void KoShapeGroup::invalidateSizeCache()
{
Q_D(KoShapeGroup);
d->sizeCached = false;
}
diff --git a/libs/flake/KoTosContainer.cpp b/libs/flake/KoTosContainer.cpp
index 94a4db184bd..3e57ee8860e 100644
--- a/libs/flake/KoTosContainer.cpp
+++ b/libs/flake/KoTosContainer.cpp
@@ -1,350 +1,352 @@
/* This file is part of the KDE project
* Copyright (C) 2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 KO GmbH <boud@kogmbh.com>
* Copyright (C) 2010 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoTosContainer.h"
#include "KoTosContainer_p.h"
#include "KoShapeRegistry.h"
#include "KoShapeFactoryBase.h"
#include "KoShapeLoadingContext.h"
#include "KoTextShapeDataBase.h"
#include "KoTosContainerModel.h"
#include "KoStyleStack.h"
#include "KoOdfLoadingContext.h"
#include "KoXmlNS.h"
#include "KoGenStyle.h"
+#include <kdebug.h>
+
#include <QTextCursor>
#include <QTextDocument>
KoTosContainerPrivate::KoTosContainerPrivate(KoShapeContainer *q)
: KoShapeContainerPrivate(q)
, resizeBehavior(KoTosContainer::IndependentSizes)
{
}
KoTosContainerPrivate::~KoTosContainerPrivate()
{
}
KoTosContainer::KoTosContainer()
: KoShapeContainer(*(new KoTosContainerPrivate(this)))
{
}
KoTosContainer::~KoTosContainer()
{
delete textShape();
}
KoTosContainer::KoTosContainer(KoTosContainerPrivate &dd)
: KoShapeContainer(dd)
{
}
void KoTosContainer::paintComponent(QPainter &, const KoViewConverter &, KoShapePaintingContext &)
{
}
bool KoTosContainer::loadText(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(const KoTosContainer);
KoXmlElement child;
forEachElement(child, element) {
// only recreate the text shape if there's something to be loaded
if (child.localName() == "p" || child.localName() == "list") {
KoShape *textShape = createTextShape(context.documentResourceManager());
if (!textShape) {
return false;
}
//apply the style properties to the loaded text
setTextAlignment(d->alignment);
// In the case of text on shape, we cannot ask the text shape to load
// the odf, since it expects a complete document with style info and
// everything, so we have to use the KoTextShapeData object instead.
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
Q_ASSERT(shapeData);
shapeData->loadStyle(element, context);
bool loadOdf = shapeData->loadOdf(element, context);
return loadOdf;
}
}
return true;
}
void KoTosContainer::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(KoTosContainer);
KoShapeContainer::loadStyle(element, context);
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.setTypeProperties("graphic");
QString verticalAlign(styleStack.property(KoXmlNS::draw, "textarea-vertical-align"));
Qt::Alignment vAlignment(Qt::AlignTop);
if (verticalAlign == "bottom") {
vAlignment = Qt::AlignBottom;
} else if (verticalAlign == "justify") {
// not yet supported
vAlignment = Qt::AlignVCenter;
} else if (verticalAlign == "middle") {
vAlignment = Qt::AlignVCenter;
}
QString horizontalAlign(styleStack.property(KoXmlNS::draw, "textarea-horizontal-align"));
Qt::Alignment hAlignment(Qt::AlignLeft);
if (horizontalAlign == "center") {
hAlignment = Qt::AlignCenter;
} else if (horizontalAlign == "justify") {
// not yet supported
hAlignment = Qt::AlignCenter;
} else if (horizontalAlign == "right") {
hAlignment = Qt::AlignRight;
}
d->alignment = vAlignment | hAlignment;
}
QString KoTosContainer::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
{
Qt::Alignment alignment = textAlignment();
QString verticalAlignment = "top";
Qt::Alignment vAlignment(alignment & Qt::AlignVertical_Mask);
if (vAlignment == Qt::AlignBottom) {
verticalAlignment = "bottom";
} else if (vAlignment == Qt::AlignVCenter || vAlignment == Qt::AlignCenter) {
verticalAlignment = "middle";
}
style.addProperty("draw:textarea-vertical-align", verticalAlignment);
QString horizontalAlignment = "left";
Qt::Alignment hAlignment(alignment & Qt::AlignHorizontal_Mask);
if (hAlignment == Qt::AlignCenter || hAlignment == Qt::AlignHCenter) {
horizontalAlignment = "center";
} else if (hAlignment == Qt::AlignJustify) {
horizontalAlignment = "justify";
} else if (hAlignment == Qt::AlignRight) {
horizontalAlignment = "right";
}
style.addProperty("draw:textarea-horizontal-align", horizontalAlignment);
return KoShapeContainer::saveStyle(style, context);
}
void KoTosContainer::saveText(KoShapeSavingContext &context) const
{
KoShape *textShape = this->textShape();
if (!textShape) {
return;
}
// In the case of text on shape, we cannot ask the text shape to save
// the odf, since it would save all the frame information as well, which
// is wrong.
// Only save the text shape if it has content.
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
if (shapeData && !shapeData->document()->isEmpty()) {
shapeData->saveOdf(context);
}
}
void KoTosContainer::setPlainText(const QString &text)
{
KoShape *textShape = this->textShape();
if (textShape == 0) {
kWarning(30006) << "No text shape present in KoTosContainer";
return;
}
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
Q_ASSERT(shapeData->document());
shapeData->document()->setPlainText(text);
}
void KoTosContainer::setResizeBehavior(ResizeBehavior resizeBehavior)
{
Q_D(KoTosContainer);
if (d->resizeBehavior == resizeBehavior) {
return;
}
d->resizeBehavior = resizeBehavior;
if (d->model) {
d->model->containerChanged(this, KoShape::SizeChanged);
}
}
KoTosContainer::ResizeBehavior KoTosContainer::resizeBehavior() const
{
Q_D(const KoTosContainer);
return d->resizeBehavior;
}
void KoTosContainer::setTextAlignment(Qt::Alignment alignment)
{
Q_D(KoTosContainer);
KoShape *textShape = this->textShape();
if (textShape == 0) {
kWarning(30006) << "No text shape present in KoTosContainer";
return;
}
// vertical
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
shapeData->setVerticalAlignment(alignment);
// horizontal
Q_ASSERT(shapeData->document());
QTextBlockFormat bf;
bf.setAlignment(alignment & Qt::AlignHorizontal_Mask);
QTextCursor cursor(shapeData->document());
cursor.setPosition(QTextCursor::End, QTextCursor::KeepAnchor);
cursor.mergeBlockFormat(bf);
d->alignment = alignment;
}
Qt::Alignment KoTosContainer::textAlignment() const
{
KoShape *textShape = this->textShape();
if (textShape == 0) {
kWarning(30006) << "No text shape present in KoTosContainer";
return Qt::AlignTop;
}
// vertical
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
// the model makes sure it contains a shape that has a KoTextShapeDataBase set so no need to check that
Qt::Alignment answer = shapeData->verticalAlignment() & Qt::AlignVertical_Mask;
// horizontal
Q_ASSERT(shapeData->document());
QTextCursor cursor(shapeData->document());
answer = answer | (cursor.blockFormat().alignment() & Qt::AlignHorizontal_Mask);
return answer;
}
void KoTosContainer::setPreferredTextRect(const QRectF &rect)
{
Q_D(KoTosContainer);
d->preferredTextRect = rect;
KoShape *textShape = this->textShape();
//kDebug(30006) << rect << textShape << d->resizeBehavior;
if (d->resizeBehavior == TextFollowsPreferredTextRect && textShape) {
//kDebug(30006) << rect;
textShape->setPosition(rect.topLeft());
textShape->setSize(rect.size());
}
}
QRectF KoTosContainer::preferredTextRect() const
{
Q_D(const KoTosContainer);
return d->preferredTextRect;
}
KoShape *KoTosContainer::createTextShape(KoDocumentResourceManager *documentResources)
{
if (!documentResources) {
kWarning(30006) << "KoDocumentResourceManager not found";
return 0;
}
Q_D(KoTosContainer);
delete textShape();
delete d->model;
d->model = new KoTosContainerModel();
QSet<KoShape*> delegates;
delegates << this;
KoShape *textShape = 0;
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get("TextShapeID");
if (factory) { // not installed, thats too bad, but allowed
textShape = factory->createDefaultShape(documentResources);
Q_ASSERT(textShape); // would be a bug in the text shape;
if (d->resizeBehavior == TextFollowsPreferredTextRect) {
textShape->setSize(d->preferredTextRect.size());
} else {
textShape->setSize(size());
}
if (d->resizeBehavior == TextFollowsPreferredTextRect) {
textShape->setPosition(d->preferredTextRect.topLeft());
} else {
textShape->setPosition(QPointF(0, 0));
}
textShape->setSelectable(false);
textShape->setRunThrough(runThrough());
KoTextShapeDataBase *shapeData = qobject_cast<KoTextShapeDataBase*>(textShape->userData());
Q_ASSERT(shapeData); // would be a bug in kotext
// TODO check if that is correct depending on the resize mode
shapeData->setVerticalAlignment(Qt::AlignVCenter);
addShape(textShape);
// textShape->setZIndex(zIndex() + 1); // not needed as there as the text shape is the only sub shape
delegates << textShape;
} else {
kWarning(30006) << "Text shape factory not found";
}
setToolDelegates(delegates);
return textShape;
}
KoShape *KoTosContainer::textShape() const
{
const QList<KoShape*> subShapes = shapes();
return subShapes.isEmpty() ? 0 : subShapes.at(0);
}
void KoTosContainer::shapeChanged(ChangeType type, KoShape *shape)
{
Q_UNUSED(shape);
Q_D(KoTosContainer);
if (d->model == 0) {
return;
}
if (type == SizeChanged || type == ContentChanged) {
d->model->containerChanged(this, type);
}
// TODO is this needed?
#if 0
foreach(KoShape *shape, d->model->shapes())
shape->notifyChanged();
#endif
}
void KoTosContainer::setRunThrough(short int runThrough)
{
KoShape::setRunThrough(runThrough);
KoShape *textShape = this->textShape();
if (textShape) {
textShape->setRunThrough(runThrough);
}
}
diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp
index cf12d4a91e8..20ee6417a99 100644
--- a/libs/pigment/KoColorSpaceRegistry.cpp
+++ b/libs/pigment/KoColorSpaceRegistry.cpp
@@ -1,652 +1,649 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004,2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoColorSpaceRegistry.h"
#include <QHash>
#include <QReadWriteLock>
#include <QStringList>
#include <QDir>
-#include <kcomponentdata.h>
-#include <kmessagebox.h>
-#include <klocale.h>
#include <kglobal.h>
#include "KoPluginLoader.h"
#include "KoGenericRegistry.h"
#include "DebugPigment.h"
#include "KoBasicHistogramProducers.h"
#include "KoColorSpace.h"
#include "KoColorProfile.h"
#include "KoColorConversionCache.h"
#include "KoColorConversionSystem.h"
#include "colorspaces/KoAlphaColorSpace.h"
#include "colorspaces/KoLabColorSpace.h"
#include "colorspaces/KoRgbU16ColorSpace.h"
#include "colorspaces/KoRgbU8ColorSpace.h"
#include "colorspaces/KoSimpleColorSpaceEngine.h"
#include "KoColorSpace_p.h"
struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private {
KoGenericRegistry<KoColorSpaceFactory *> colorSpaceFactoryRegistry;
QList<KoColorSpaceFactory *> localFactories;
QHash<QString, KoColorProfile * > profileMap;
QHash<QString, QString> profileAlias;
QHash<QString, const KoColorSpace * > csMap;
KoColorConversionSystem *colorConversionSystem;
KoColorConversionCache* colorConversionCache;
KoColorSpaceFactory* alphaCSF;
const KoColorSpace *rgbU8sRGB;
const KoColorSpace *lab16sLAB;
const KoColorSpace *alphaCs;
QReadWriteLock registrylock;
};
KoColorSpaceRegistry* KoColorSpaceRegistry::instance()
{
K_GLOBAL_STATIC(KoColorSpaceRegistry, s_instance);
if (!s_instance.exists()) {
s_instance->init();
}
return s_instance;
}
void KoColorSpaceRegistry::init()
{
d->rgbU8sRGB = 0;
d->lab16sLAB = 0;
d->alphaCs = 0;
d->colorConversionSystem = new KoColorConversionSystem;
d->colorConversionCache = new KoColorConversionCache;
KoColorSpaceEngineRegistry::instance()->add(new KoSimpleColorSpaceEngine());
addProfile(new KoDummyColorProfile);
// Create the built-in colorspaces
d->localFactories << new KoLabColorSpaceFactory()
<< new KoRgbU8ColorSpaceFactory()
<< new KoRgbU16ColorSpaceFactory();
foreach(KoColorSpaceFactory *factory, d->localFactories) {
add(factory);
}
d->alphaCs = new KoAlphaColorSpace();
d->alphaCs->d->deletability = OwnedByRegistryRegistryDeletes;
KoPluginLoader::PluginsConfig config;
config.whiteList = "ColorSpacePlugins";
config.blacklist = "ColorSpacePluginsDisabled";
config.group = "calligra";
KoPluginLoader::instance()->load("Calligra/ColorSpace", "[X-Pigment-PluginVersion] == 28", config);
KoPluginLoader::PluginsConfig configExtensions;
configExtensions.whiteList = "ColorSpaceExtensionsPlugins";
configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled";
configExtensions.group = "calligra";
KoPluginLoader::instance()->load("Calligra/ColorSpaceExtension", "[X-Pigment-PluginVersion] == 28", configExtensions);
dbgPigment << "Loaded the following colorspaces:";
foreach(const KoID& id, listKeys()) {
dbgPigment << "\t" << id.id() << "," << id.name();
}
}
KoColorSpaceRegistry::KoColorSpaceRegistry() : d(new Private())
{
d->colorConversionSystem = 0;
d->colorConversionCache = 0;
}
KoColorSpaceRegistry::~KoColorSpaceRegistry()
{
// Just leak on exit... It's faster.
// delete d->colorConversionSystem;
// foreach(KoColorProfile* profile, d->profileMap) {
// delete profile;
// }
// d->profileMap.clear();
// foreach(const KoColorSpace * cs, d->csMap) {
// cs->d->deletability = OwnedByRegistryRegistryDeletes;
// }
// d->csMap.clear();
// // deleting colorspaces calls a function in the cache
// delete d->colorConversionCache;
// d->colorConversionCache = 0;
// // Delete the colorspace factories
// qDeleteAll(d->localFactories);
// delete d->alphaCSF;
delete d;
}
void KoColorSpaceRegistry::add(KoColorSpaceFactory* item)
{
{
QWriteLocker l(&d->registrylock);
d->colorSpaceFactoryRegistry.add(item);
}
d->colorConversionSystem->insertColorSpace(item);
}
void KoColorSpaceRegistry::remove(KoColorSpaceFactory* item)
{
d->registrylock.lockForRead();
QList<QString> toremove;
foreach(const KoColorSpace * cs, d->csMap) {
if (cs->id() == item->id()) {
toremove.push_back(idsToCacheName(cs->id(), cs->profile()->name()));
cs->d->deletability = OwnedByRegistryRegistryDeletes;
}
}
d->registrylock.unlock();
d->registrylock.lockForWrite();
foreach(const QString& id, toremove) {
d->csMap.remove(id);
// TODO: should not it delete the color space when removing it from the map ?
}
d->colorSpaceFactoryRegistry.remove(item->id());
d->registrylock.unlock();
}
void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to)
{
QWriteLocker l(&d->registrylock);
d->profileAlias[name] = to;
}
QString KoColorSpaceRegistry::profileAlias(const QString& _name) const
{
QReadLocker l(&d->registrylock);
return d->profileAlias.value(_name, _name);
}
const KoColorProfile * KoColorSpaceRegistry::profileByName(const QString & _name) const
{
QReadLocker l(&d->registrylock);
return d->profileMap.value( profileAlias(_name), 0);
}
QList<const KoColorProfile *> KoColorSpaceRegistry::profilesFor(const QString &id) const
{
return profilesFor(d->colorSpaceFactoryRegistry.value(id));
}
const KoColorSpace * KoColorSpaceRegistry::colorSpace(const KoID &csID, const QString & profileName)
{
return colorSpace(csID.id(), profileName);
}
const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile)
{
return colorSpace(colorSpaceId(colorModelId, colorDepthId), profile);
}
const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName)
{
return colorSpace(colorSpaceId(colorModelId, colorDepthId), profileName);
}
QList<const KoColorProfile *> KoColorSpaceRegistry::profilesFor(const KoColorSpaceFactory * csf) const
{
QReadLocker l(&d->registrylock);
QList<const KoColorProfile *> profiles;
if (csf == 0)
return profiles;
QHash<QString, KoColorProfile * >::Iterator it;
for (it = d->profileMap.begin(); it != d->profileMap.end(); ++it) {
KoColorProfile * profile = it.value();
if (csf->profileIsCompatible(profile)) {
Q_ASSERT(profile);
// if (profile->colorSpaceSignature() == csf->colorSpaceSignature()) {
profiles.push_back(profile);
}
}
return profiles;
}
QList<const KoColorSpaceFactory*> KoColorSpaceRegistry::colorSpacesFor(const KoColorProfile* _profile) const
{
QReadLocker l(&d->registrylock);
QList<const KoColorSpaceFactory*> csfs;
foreach(KoColorSpaceFactory* csf, d->colorSpaceFactoryRegistry.values()) {
if (csf->profileIsCompatible(_profile)) {
csfs.push_back(csf);
}
}
return csfs;
}
QList<const KoColorProfile *> KoColorSpaceRegistry::profilesFor(const KoID& id) const
{
return profilesFor(id.id());
}
void KoColorSpaceRegistry::addProfileToMap(KoColorProfile *p)
{
Q_ASSERT(p);
if (p->valid()) {
d->profileMap[p->name()] = p;
}
}
void KoColorSpaceRegistry::addProfile(KoColorProfile *p)
{
Q_ASSERT(p);
if (p->valid()) {
d->profileMap[p->name()] = p;
d->colorConversionSystem->insertColorProfile(p);
}
}
void KoColorSpaceRegistry::addProfile(const KoColorProfile* profile)
{
addProfile(profile->clone());
}
void KoColorSpaceRegistry::removeProfile(KoColorProfile* profile)
{
d->profileMap.remove(profile->name());
}
bool KoColorSpaceRegistry::isCached(const QString & csID, const QString & profileName) const
{
QReadLocker l(&d->registrylock);
return !(d->csMap.find(idsToCacheName(csID, profileName)) == d->csMap.end());
}
QString KoColorSpaceRegistry::idsToCacheName(const QString & csID, const QString & profileName) const
{
return csID + "<comb>" + profileName;
}
const KoColorSpaceFactory* KoColorSpaceRegistry::colorSpaceFactory(const QString &colorSpaceId) const
{
QReadLocker l(&d->registrylock);
return d->colorSpaceFactoryRegistry.get(colorSpaceId);
}
const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString &csID, const QString &pName)
{
QString profileName = pName;
if (profileName.isEmpty()) {
QReadLocker l(&d->registrylock);
KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(csID);
if (!csf) {
dbgPigmentCSRegistry << "Unknown color space type : " << csID;
return 0;
}
profileName = csf->defaultProfile();
}
if (profileName.isEmpty()) {
return 0;
}
if (!isCached(csID, profileName)) {
d->registrylock.lockForRead();
KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(csID);
d->registrylock.unlock();
if (!csf) {
dbgPigmentCSRegistry << "Unknown color space type :" << csf;
return 0;
}
// last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available.
const KoColorProfile *p = profileByName(profileName);
if (!p) {
dbgPigmentCSRegistry << "Profile not found :" << profileName;
/**
* If the requested profile is not available, try fetching the
* default one
*/
profileName = csf->defaultProfile();
p = profileByName(profileName);
/**
* If there is no luck, try to fetch the first one
*/
if (!p) {
QList<const KoColorProfile *> profiles = profilesFor(csID);
if (!profiles.isEmpty()) {
p = profiles[0];
Q_ASSERT(p);
}
}
}
// We did our best, but still have no profile: and since csf->grabColorSpace
// needs the profile to find the colorspace, we have to give up.
if (!p) {
return 0;
}
profileName = p->name();
const KoColorSpace *cs = csf->grabColorSpace(p);
if (!cs) {
dbgPigmentCSRegistry << "Unable to create color space";
return 0;
}
QWriteLocker l(&d->registrylock);
dbgPigmentCSRegistry << "colorspace count: " << d->csMap.count()
<< ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name())
<< "\n\tcsID" << csID
<< "\n\tprofileName" << profileName
<< "\n\tcs->id()" << cs->id()
<< "\n\tcs->profile()->name()" << cs->profile()->name()
<< "\n\tpName" << pName;
Q_ASSERT(cs->id() == csID);
Q_ASSERT(cs->profile()->name() == profileName);
d->csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs;
cs->d->deletability = OwnedByRegistryDoNotDelete;
}
QReadLocker l(&d->registrylock);
if (d->csMap.contains(idsToCacheName(csID, profileName))) {
const KoColorSpace *cs = d->csMap[idsToCacheName(csID, profileName)];
Q_ASSERT(cs->profile()->name() == profileName);
return cs;
}
else {
return 0;
}
}
const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString &csID, const KoColorProfile *profile)
{
if (csID.isEmpty()) {
return 0;
}
if (profile) {
const KoColorSpace *cs = 0;
if (isCached(csID, profile->name())) {
cs = colorSpace(csID, profile->name());
}
if (!d->profileMap.contains(profile->name())) {
addProfile(profile);
}
if (!cs) {
// The profile was not stored and thus not the combination either
d->registrylock.lockForRead();
KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(csID);
d->registrylock.unlock();
if (!csf) {
dbgPigmentCSRegistry << "Unknown color space type :" << csf;
return 0;
}
if (!csf->profileIsCompatible(profile ) ) {
return 0;
}
cs = csf->grabColorSpace(profile);
if (!cs)
return 0;
QWriteLocker l(&d->registrylock);
QString name = csID + "<comb>" + profile->name();
d->csMap[name] = cs;
cs->d->deletability = OwnedByRegistryDoNotDelete;
dbgPigmentCSRegistry << "colorspace count: " << d->csMap.count() << ", adding name: " << name;
}
return cs;
} else {
return colorSpace(csID);
}
}
const KoColorSpace * KoColorSpaceRegistry::alpha8()
{
if (!d->alphaCs) {
d->alphaCs = colorSpace(KoAlphaColorSpace::colorSpaceId());
}
Q_ASSERT(d->alphaCs);
return d->alphaCs;
}
const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName)
{
if (profileName.isEmpty()) {
if (!d->rgbU8sRGB) {
d->rgbU8sRGB = colorSpace(KoRgbU8ColorSpace::colorSpaceId());
}
Q_ASSERT(d->rgbU8sRGB);
return d->rgbU8sRGB;
}
return colorSpace(KoRgbU8ColorSpace::colorSpaceId(), profileName);
}
const KoColorSpace * KoColorSpaceRegistry::rgb8(const KoColorProfile * profile)
{
if (profile == 0) {
if (!d->rgbU8sRGB) {
d->rgbU8sRGB = colorSpace(KoRgbU8ColorSpace::colorSpaceId());
}
Q_ASSERT(d->rgbU8sRGB);
return d->rgbU8sRGB;
}
return colorSpace(KoRgbU8ColorSpace::colorSpaceId(), profile);
}
const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName)
{
return colorSpace(KoRgbU16ColorSpace::colorSpaceId(), profileName);
}
const KoColorSpace * KoColorSpaceRegistry::rgb16(const KoColorProfile * profile)
{
return colorSpace(KoRgbU16ColorSpace::colorSpaceId(), profile);
}
const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName)
{
if (profileName.isEmpty()) {
if (!d->lab16sLAB) {
d->lab16sLAB = colorSpace(KoLabColorSpace::colorSpaceId(), profileName);
}
return d->lab16sLAB;
}
return colorSpace(KoLabColorSpace::colorSpaceId(), profileName);
}
const KoColorSpace * KoColorSpaceRegistry::lab16(const KoColorProfile * profile)
{
if (profile == 0) {
if (!d->lab16sLAB) {
d->lab16sLAB = colorSpace(KoLabColorSpace::colorSpaceId(), profile);
}
Q_ASSERT(d->lab16sLAB);
return d->lab16sLAB;
}
return colorSpace(KoLabColorSpace::colorSpaceId(), profile);
}
QList<KoID> KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const
{
QReadLocker l(&d->registrylock);
QList<KoID> ids;
QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
foreach(KoColorSpaceFactory* factory, factories) {
if (!ids.contains(factory->colorModelId())
&& (option == AllColorSpaces || factory->userVisible())) {
ids << factory->colorModelId();
}
}
return ids;
}
QList<KoID> KoColorSpaceRegistry::colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const
{
return colorDepthList(colorModelId.id(), option);
}
QList<KoID> KoColorSpaceRegistry::colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const
{
QReadLocker l(&d->registrylock);
QList<KoID> ids;
QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
foreach(KoColorSpaceFactory* factory, factories) {
if (!ids.contains(KoID(factory->colorDepthId()))
&& factory->colorModelId().id() == colorModelId
&& (option == AllColorSpaces || factory->userVisible())) {
ids << factory->colorDepthId();
}
}
return ids;
}
QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const
{
QReadLocker l(&d->registrylock);
QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
foreach(KoColorSpaceFactory* factory, factories) {
if (factory->colorModelId().id() == colorModelId && factory->colorDepthId().id() == colorDepthId) {
return factory->id();
}
}
return "";
}
QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const
{
return colorSpaceId(colorModelId.id(), colorDepthId.id());
}
KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const
{
QReadLocker l(&d->registrylock);
KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
if (factory) {
return factory->colorModelId();
} else {
return KoID();
}
}
KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const
{
QReadLocker l(&d->registrylock);
KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId);
if (factory) {
return factory->colorDepthId();
} else {
return KoID();
}
}
const KoColorConversionSystem* KoColorSpaceRegistry::colorConversionSystem() const
{
return d->colorConversionSystem;
}
KoColorConversionCache* KoColorSpaceRegistry::colorConversionCache() const
{
return d->colorConversionCache;
}
const KoColorSpace* KoColorSpaceRegistry::permanentColorspace(const KoColorSpace* _colorSpace)
{
if (_colorSpace->d->deletability != NotOwnedByRegistry) {
return _colorSpace;
} else if (*_colorSpace == *d->alphaCs) {
return d->alphaCs;
} else {
const KoColorSpace* cs = colorSpace(_colorSpace->id(), _colorSpace->profile());
Q_ASSERT(cs);
Q_ASSERT(*cs == *_colorSpace);
return cs;
}
}
QList<KoID> KoColorSpaceRegistry::listKeys() const
{
QReadLocker l(&d->registrylock);
QList<KoID> answer;
foreach(const QString& key, d->colorSpaceFactoryRegistry.keys()) {
answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name()));
}
return answer;
}
const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData)
{
QReadLocker l(&d->registrylock);
KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(colorSpaceId(colorModelId, colorDepthId));
return factory_->colorProfile(rawData);
}
QList<const KoColorSpace*> KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection)
{
QList<const KoColorSpace*> colorSpaces;
d->registrylock.lockForRead();
QList<KoColorSpaceFactory*> factories = d->colorSpaceFactoryRegistry.values();
d->registrylock.unlock();
foreach(KoColorSpaceFactory* factory, factories) {
// Don't test with ycbcr for now, since we don't have a default profile for it.
if (factory->colorModelId().id().startsWith("Y")) continue;
if (visibility == AllColorSpaces || factory->userVisible()) {
if (pSelection == OnlyDefaultProfile) {
const KoColorSpace *cs = colorSpace(factory->id());
if (cs) {
colorSpaces.append(cs);
}
else {
warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile";
}
} else {
QList<const KoColorProfile*> profiles = KoColorSpaceRegistry::instance()->profilesFor(factory->id());
foreach(const KoColorProfile * profile, profiles) {
const KoColorSpace *cs = colorSpace(factory->id(), profile);
if (cs) {
colorSpaces.append(cs);
}
else {
warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name();
}
}
}
}
}
return colorSpaces;
}
diff --git a/libs/pigment/KoCompositeOpRegistry.h b/libs/pigment/KoCompositeOpRegistry.h
index 2c367fbf334..42ca0c62f43 100644
--- a/libs/pigment/KoCompositeOpRegistry.h
+++ b/libs/pigment/KoCompositeOpRegistry.h
@@ -1,170 +1,169 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOMPOSITEOPREGISTRY_H
#define KOCOMPOSITEOPREGISTRY_H
-#include <klocale.h>
#include <QString>
#include <QList>
#include <QMultiMap>
#include <QBitArray>
#include "pigment_export.h"
class KoColorSpace;
#include <KoID.h>
const QString COMPOSITE_OVER = "normal";
const QString COMPOSITE_ERASE = "erase";
const QString COMPOSITE_IN = "in";
const QString COMPOSITE_OUT = "out";
const QString COMPOSITE_ALPHA_DARKEN = "alphadarken";
const QString COMPOSITE_XOR = "xor";
const QString COMPOSITE_PLUS = "plus";
const QString COMPOSITE_MINUS = "minus";
const QString COMPOSITE_ADD = "add";
const QString COMPOSITE_SUBTRACT = "subtract";
const QString COMPOSITE_INVERSE_SUBTRACT = "inverse_subtract";
const QString COMPOSITE_DIFF = "diff";
const QString COMPOSITE_MULT = "multiply";
const QString COMPOSITE_DIVIDE = "divide";
const QString COMPOSITE_ARC_TANGENT = "arc_tangent";
const QString COMPOSITE_GEOMETRIC_MEAN = "geometric_mean";
const QString COMPOSITE_ADDITIVE_SUBTRACTIVE = "additive_subtractive";
const QString COMPOSITE_EQUIVALENCE = "equivalence";
const QString COMPOSITE_ALLANON = "allanon";
const QString COMPOSITE_PARALLEL = "parallel";
const QString COMPOSITE_GRAIN_MERGE = "grain_merge";
const QString COMPOSITE_GRAIN_EXTRACT = "grain_extract";
const QString COMPOSITE_EXCLUSION = "exclusion";
const QString COMPOSITE_HARD_MIX = "hard mix";
const QString COMPOSITE_OVERLAY = "overlay";
const QString COMPOSITE_BEHIND = "behind";
const QString COMPOSITE_DARKEN = "darken";
const QString COMPOSITE_BURN = "burn";//this is also known as 'color burn'.
const QString COMPOSITE_LINEAR_BURN = "linear_burn";
const QString COMPOSITE_GAMMA_DARK = "gamma_dark";
const QString COMPOSITE_LIGHTEN = "lighten";
const QString COMPOSITE_DODGE = "dodge";
const QString COMPOSITE_LINEAR_DODGE = "linear_dodge";
const QString COMPOSITE_SCREEN = "screen";
const QString COMPOSITE_HARD_LIGHT = "hard_light";
const QString COMPOSITE_SOFT_LIGHT_PHOTOSHOP = "soft_light";
const QString COMPOSITE_SOFT_LIGHT_SVG = "soft_light_svg";
const QString COMPOSITE_GAMMA_LIGHT = "gamma_light";
const QString COMPOSITE_VIVID_LIGHT = "vivid_light";
const QString COMPOSITE_LINEAR_LIGHT = "linear light";
const QString COMPOSITE_PIN_LIGHT = "pin_light";
const QString COMPOSITE_HUE = "hue";
const QString COMPOSITE_COLOR = "color";
const QString COMPOSITE_SATURATION = "saturation";
const QString COMPOSITE_INC_SATURATION = "inc_saturation";
const QString COMPOSITE_DEC_SATURATION = "dec_saturation";
const QString COMPOSITE_LUMINIZE = "luminize";
const QString COMPOSITE_INC_LUMINOSITY = "inc_luminosity";
const QString COMPOSITE_DEC_LUMINOSITY = "dec_luminosity";
const QString COMPOSITE_HUE_HSV = "hue_hsv";
const QString COMPOSITE_COLOR_HSV = "color_hsv";
const QString COMPOSITE_SATURATION_HSV = "saturation_hsv";
const QString COMPOSITE_INC_SATURATION_HSV = "inc_saturation_hsv";
const QString COMPOSITE_DEC_SATURATION_HSV = "dec_saturation_hsv";
const QString COMPOSITE_VALUE = "value";
const QString COMPOSITE_INC_VALUE = "inc_value";
const QString COMPOSITE_DEC_VALUE = "dec_value";
const QString COMPOSITE_HUE_HSL = "hue_hsl";
const QString COMPOSITE_COLOR_HSL = "color_hsl";
const QString COMPOSITE_SATURATION_HSL = "saturation_hsl";
const QString COMPOSITE_INC_SATURATION_HSL = "inc_saturation_hsl";
const QString COMPOSITE_DEC_SATURATION_HSL = "dec_saturation_hsl";
const QString COMPOSITE_LIGHTNESS = "lightness";
const QString COMPOSITE_INC_LIGHTNESS = "inc_lightness";
const QString COMPOSITE_DEC_LIGHTNESS = "dec_lightness";
const QString COMPOSITE_HUE_HSI = "hue_hsi";
const QString COMPOSITE_COLOR_HSI = "color_hsi";
const QString COMPOSITE_SATURATION_HSI = "saturation_hsi";
const QString COMPOSITE_INC_SATURATION_HSI = "inc_saturation_hsi";
const QString COMPOSITE_DEC_SATURATION_HSI = "dec_saturation_hsi";
const QString COMPOSITE_INTENSITY = "intensity";
const QString COMPOSITE_INC_INTENSITY = "inc_intensity";
const QString COMPOSITE_DEC_INTENSITY = "dec_intensity";
const QString COMPOSITE_COPY = "copy";
const QString COMPOSITE_COPY_RED = "copy_red";
const QString COMPOSITE_COPY_GREEN = "copy_green";
const QString COMPOSITE_COPY_BLUE = "copy_blue";
const QString COMPOSITE_TANGENT_NORMALMAP = "tangent_normalmap";
const QString COMPOSITE_COLORIZE = "colorize";
const QString COMPOSITE_BUMPMAP = "bumpmap";
const QString COMPOSITE_COMBINE_NORMAL = "combine_normal";
const QString COMPOSITE_CLEAR = "clear";
const QString COMPOSITE_DISSOLVE = "dissolve";
const QString COMPOSITE_DISPLACE = "displace";
const QString COMPOSITE_NO = "nocomposition";
const QString COMPOSITE_PASS_THROUGH = "pass through"; // XXX: not implemented anywhere yet
const QString COMPOSITE_DARKER_COLOR = "darker color";
const QString COMPOSITE_LIGHTER_COLOR = "lighter color";
const QString COMPOSITE_UNDEF = "undefined";
class PIGMENTCMS_EXPORT KoCompositeOpRegistry
{
typedef QMultiMap<KoID,KoID> KoIDMap;
typedef QList<KoID> KoIDList;
KoCompositeOpRegistry();
public:
static const KoCompositeOpRegistry& instance();
KoID getDefaultCompositeOp() const;
KoID getKoID(const QString& compositeOpID) const;
KoIDMap getCompositeOps() const;
KoIDList getCategories() const;
KoIDList getCompositeOps(const KoColorSpace* colorSpace) const;
KoIDList getCompositeOps(const KoID& category, const KoColorSpace* colorSpace=0) const;
bool colorSpaceHasCompositeOp(const KoColorSpace* colorSpace, const KoID& compositeOp) const;
template<class TKoIdIterator>
KoIDList filterCompositeOps(TKoIdIterator begin, TKoIdIterator end, const KoColorSpace* colorSpace, bool removeInvaliOps=true) const {
KoIDList list;
for(; begin!=end; ++begin){
if( colorSpaceHasCompositeOp(colorSpace, *begin) == removeInvaliOps)
list.push_back(*begin);
}
return list;
}
private:
KoIDList m_categories;
KoIDMap m_map;
};
#endif // KOCOMPOSITEOPREGISTRY_H
diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp
index 4f7874986a8..4a0e94ca7e6 100644
--- a/libs/pigment/resources/KoColorSet.cpp
+++ b/libs/pigment/resources/KoColorSet.cpp
@@ -1,529 +1,529 @@
/* This file is part of the KDE project
Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "KoColorSet.h"
#include <sys/types.h>
#include <netinet/in.h> // htonl
#include <QImage>
#include <QPoint>
#include <QVector>
#include <QFile>
#include <QTextStream>
#include <QFileInfo>
#include <QBuffer>
#include <QByteArray>
#include <QPainter>
#include <DebugPigment.h>
#include <klocale.h>
#include "KoColor.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorModelStandardIds.h"
KoColorSet::PaletteType detectFormat(const QString &fileName, const QByteArray &ba) {
QFileInfo fi(fileName);
// .pal
if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) {
return KoColorSet::RIFF_PAL;
}
// .gpl
else if (ba.startsWith("GIMP Palette")) {
return KoColorSet::GPL;
}
// .pal
else if (ba.startsWith("JASC-PAL")) {
return KoColorSet::PSP_PAL;
}
else if (fi.suffix().toLower() == "aco") {
return KoColorSet::ACO;
}
else if (fi.suffix().toLower() == "act") {
return KoColorSet::ACT;
}
return KoColorSet::UNKNOWN;
}
KoColorSet::KoColorSet(const QString& filename)
: KoResource(filename)
{
// Implemented in KoResource class
m_columns = 0; // Set the default value that the GIMP uses...
}
KoColorSet::KoColorSet()
: KoResource("")
{
m_columns = 0; // Set the default value that the GIMP uses...
}
/// Create an copied palette
KoColorSet::KoColorSet(const KoColorSet& rhs)
: QObject(0)
, KoResource("")
{
setFilename(rhs.filename());
m_ownData = false;
m_name = rhs.m_name;
m_comment = rhs.m_comment;
m_columns = rhs.m_columns;
m_colors = rhs.m_colors;
setValid(true);
}
KoColorSet::~KoColorSet()
{
}
bool KoColorSet::load()
{
QFile file(filename());
if (file.size() == 0) return false;
if (!file.open(QIODevice::ReadOnly)) {
- kWarning() << "Can't open file " << filename();
+ warnPigment << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KoColorSet::loadFromDevice(QIODevice *dev)
{
if (!dev->isOpen()) dev->open(QIODevice::ReadOnly);
m_data = dev->readAll();
Q_ASSERT(m_data.size() != 0);
return init();
}
bool KoColorSet::save()
{
QFile file(filename());
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return false;
}
saveToDevice(&file);
file.close();
return true;
}
qint32 KoColorSet::nColors()
{
return m_colors.count();
}
bool KoColorSet::saveToDevice(QIODevice *dev) const
{
QTextStream stream(dev);
stream << "GIMP Palette\nName: " << name() << "\nColumns: " << m_columns << "\n#\n";
for (int i = 0; i < m_colors.size(); i++) {
const KoColorSetEntry& entry = m_colors.at(i);
QColor c = entry.color.toQColor();
stream << c.red() << " " << c.green() << " " << c.blue() << "\t";
if (entry.name.isEmpty())
stream << "Untitled\n";
else
stream << entry.name << "\n";
}
KoResource::saveToDevice(dev);
return true;
}
bool KoColorSet::init()
{
m_colors.clear(); // just in case this is a reload (eg by KoEditColorSetDialog),
if (filename().isNull()) {
warnPigment << "Cannot load palette" << name() << "there is no filename set";
return false;
}
if (m_data.isNull()) {
QFile file(filename());
if (file.size() == 0) {
warnPigment << "Cannot load palette" << name() << "there is no data available";
return false;
}
file.open(QIODevice::ReadOnly);
m_data = file.readAll();
file.close();
}
bool res = false;
PaletteType paletteType = detectFormat(filename(), m_data);
switch(paletteType) {
case GPL:
res = loadGpl();
break;
case ACT:
res = loadAct();
break;
case RIFF_PAL:
res = loadRiff();
break;
case PSP_PAL:
res = loadPsp();
break;
case ACO:
res = loadAco();
break;
default:
res = false;
}
setValid(res);
if (m_columns == 0) {
m_columns = 10;
}
QImage img(m_columns * 4, (m_colors.size() / m_columns) * 4, QImage::Format_ARGB32);
QPainter gc(&img);
gc.fillRect(img.rect(), Qt::darkGray);
int counter = 0;
for(int i = 0; i < m_columns; ++i) {
for (int j = 0; j < (m_colors.size() / m_columns); ++j) {
if (counter < m_colors.size()) {
QColor c = m_colors.at(counter).color.toQColor();
gc.fillRect(i * 4, j * 4, 4, 4, c);
counter++;
}
else {
break;
}
}
}
setImage(img);
// save some memory
m_data.clear();
return res;
}
void KoColorSet::add(const KoColorSetEntry & c)
{
m_colors.push_back(c);
}
void KoColorSet::remove(const KoColorSetEntry & c)
{
QVector<KoColorSetEntry>::iterator it = m_colors.begin();
QVector<KoColorSetEntry>::iterator end = m_colors.end();
while (it != end) {
if ((*it) == c) {
m_colors.erase(it);
return;
}
++it;
}
}
void KoColorSet::removeAt(quint32 index)
{
m_colors.remove(index);
}
KoColorSetEntry KoColorSet::getColor(quint32 index)
{
return m_colors[index];
}
void KoColorSet::setColumnCount(int columns)
{
m_columns = columns;
}
int KoColorSet::columnCount()
{
return m_columns;
}
QString KoColorSet::defaultFileExtension() const
{
return QString(".gpl");
}
bool KoColorSet::loadGpl()
{
QString s = QString::fromUtf8(m_data.data(), m_data.count());
if (s.isEmpty() || s.isNull() || s.length() < 50) {
- kWarning(30009) << "Illegal Gimp palette file: " << filename();
+ warnPigment << "Illegal Gimp palette file: " << filename();
return false;
}
quint32 index = 0;
QStringList lines = s.split('\n', QString::SkipEmptyParts);
if (lines.size() < 3) {
return false;
}
QString columns;
qint32 r, g, b;
KoColorSetEntry e;
// Read name
if (!lines[0].startsWith("GIMP") || !lines[1].startsWith("Name: ")) {
- kWarning(30009) << "Illegal Gimp palette file: " << filename();
+ warnPigment << "Illegal Gimp palette file: " << filename();
return false;
}
setName(i18n(lines[1].mid(strlen("Name: ")).trimmed().toLatin1()));
index = 2;
// Read columns
if (lines[index].startsWith("Columns: ")) {
columns = lines[index].mid(strlen("Columns: ")).trimmed();
m_columns = columns.toInt();
index = 3;
}
for (qint32 i = index; i < lines.size(); i++) {
if (lines[i].startsWith('#')) {
m_comment += lines[i].mid(1).trimmed() + ' ';
} else if (!lines[i].isEmpty()) {
QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts);
if (a.count() < 3) {
break;
}
r = a[0].toInt();
a.pop_front();
g = a[0].toInt();
a.pop_front();
b = a[0].toInt();
a.pop_front();
r = qBound(0, r, 255);
g = qBound(0, g, 255);
b = qBound(0, b, 255);
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
e.color.fromQColor(QColor(r, g, b));
QString name = a.join(" ");
e.name = name.isEmpty() ? i18n("Untitled") : name;
add(e);
}
}
return true;
}
bool KoColorSet::loadAct()
{
QFileInfo info(filename());
setName(info.baseName());
KoColorSetEntry e;
for (int i = 0; i < m_data.size(); i += 3) {
quint8 r = m_data[i];
quint8 g = m_data[i+1];
quint8 b = m_data[i+2];
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
e.color.fromQColor(QColor(r, g, b));
add(e);
}
return true;
}
struct RiffHeader {
quint32 riff;
quint32 size;
quint32 signature;
quint32 data;
quint32 datasize;
quint16 version;
quint16 colorcount;
};
bool KoColorSet::loadRiff()
{
// http://worms2d.info/Palette_file
QFileInfo info(filename());
setName(info.baseName());
KoColorSetEntry e;
RiffHeader header;
memcpy(&header, m_data.constData(), sizeof(RiffHeader));
header.colorcount = ntohl(header.colorcount);
for (int i = sizeof(RiffHeader);
(i < (int)(sizeof(RiffHeader) + header.colorcount) && i < m_data.size());
i += 4) {
quint8 r = m_data[i];
quint8 g = m_data[i+1];
quint8 b = m_data[i+2];
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
e.color.fromQColor(QColor(r, g, b));
add(e);
}
return true;
}
bool KoColorSet::loadPsp()
{
QFileInfo info(filename());
setName(info.baseName());
KoColorSetEntry e;
qint32 r, g, b;
QString s = QString::fromUtf8(m_data.data(), m_data.count());
QStringList l = s.split('\n', QString::SkipEmptyParts);
if (l.size() < 4) return false;
if (l[0] != "JASC-PAL") return false;
if (l[1] != "0100") return false;
int entries = l[2].toInt();
for (int i = 0; i < entries; ++i) {
QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts);
if (a.count() != 3) {
continue;
}
r = a[0].toInt();
a.pop_front();
g = a[0].toInt();
a.pop_front();
b = a[0].toInt();
a.pop_front();
r = qBound(0, r, 255);
g = qBound(0, g, 255);
b = qBound(0, b, 255);
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb8());
e.color.fromQColor(QColor(r, g, b));
QString name = a.join(" ");
e.name = name.isEmpty() ? i18n("Untitled") : name;
add(e);
}
return true;
}
quint16 readShort(QIODevice *io) {
quint16 val;
quint64 read = io->read((char*)&val, 2);
if (read != 2) return false;
return ntohs(val);
}
bool KoColorSet::loadAco()
{
QFileInfo info(filename());
setName(info.baseName());
QBuffer buf(&m_data);
buf.open(QBuffer::ReadOnly);
quint16 version = readShort(&buf);
quint16 numColors = readShort(&buf);
KoColorSetEntry e;
const quint16 quint16_MAX = 65535;
for (int i = 0; i < numColors && !buf.atEnd(); ++i) {
quint16 colorSpace = readShort(&buf);
quint16 ch1 = readShort(&buf);
quint16 ch2 = readShort(&buf);
quint16 ch3 = readShort(&buf);
quint16 ch4 = readShort(&buf);
bool skip = false;
if (colorSpace == 0) { // RGB
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb16());
reinterpret_cast<quint16*>(e.color.data())[0] = ch3;
reinterpret_cast<quint16*>(e.color.data())[1] = ch2;
reinterpret_cast<quint16*>(e.color.data())[2] = ch1;
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else if (colorSpace == 1) { // HSB
e.color = KoColor(KoColorSpaceRegistry::instance()->rgb16());
QColor c;
c.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0);
e.color.fromQColor(c);
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else if (colorSpace == 2) { // CMYK
e.color = KoColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), ""));
reinterpret_cast<quint16*>(e.color.data())[0] = quint16_MAX - ch1;
reinterpret_cast<quint16*>(e.color.data())[1] = quint16_MAX - ch2;
reinterpret_cast<quint16*>(e.color.data())[2] = quint16_MAX - ch3;
reinterpret_cast<quint16*>(e.color.data())[3] = quint16_MAX - ch4;
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else if (colorSpace == 7) { // LAB
e.color = KoColor(KoColorSpaceRegistry::instance()->lab16());
reinterpret_cast<quint16*>(e.color.data())[0] = ch3;
reinterpret_cast<quint16*>(e.color.data())[1] = ch2;
reinterpret_cast<quint16*>(e.color.data())[2] = ch1;
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else if (colorSpace == 8) { // GRAY
e.color = KoColor(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), ""));
reinterpret_cast<quint16*>(e.color.data())[0] = ch1 * (quint16_MAX / 10000);
e.color.setOpacity(OPACITY_OPAQUE_U8);
}
else {
- kWarning() << "Unsupported colorspace in palette" << filename() << "(" << colorSpace << ")";
+ warnPigment << "Unsupported colorspace in palette" << filename() << "(" << colorSpace << ")";
skip = true;
}
if (version == 2) {
quint16 v2 = readShort(&buf);
if (v2 != 2) {
- kWarning() << "Version 2 block is not version 2" << filename() << "(" << v2 << ")";
+ warnPigment << "Version 2 block is not version 2" << filename() << "(" << v2 << ")";
return false;
}
quint16 size = readShort(&buf);
QByteArray ba = buf.read(size);
if (ba.size() != size) {
- kWarning() << "Version 2 name block is the wrong size" << filename();
+ warnPigment << "Version 2 name block is the wrong size" << filename();
return false;
}
e.name = QString::fromUtf8(ba.constData(), ba.size());
}
if (!skip) {
add(e);
}
}
return true;
}
diff --git a/libs/pigment/resources/KoSegmentGradient.cpp b/libs/pigment/resources/KoSegmentGradient.cpp
index b559daa1478..3aa1ad0d7a9 100644
--- a/libs/pigment/resources/KoSegmentGradient.cpp
+++ b/libs/pigment/resources/KoSegmentGradient.cpp
@@ -1,902 +1,902 @@
/*
Copyright (c) 2000 Matthias Elter <elter@kde.org>
2001 John Califf
2004 Boudewijn Rempt <boud@valdyas.org>
2004 Adrian Page <adrian@pagenet.plus.com>
2004, 2007 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "KoSegmentGradient.h"
#include <cfloat>
#include <cmath>
#include <QImage>
#include <QTextStream>
#include <QFile>
#include <QByteArray>
#include <QBuffer>
#include "KoColorSpaceRegistry.h"
#include "KoColorSpace.h"
#include "KoMixColorsOp.h"
#include <DebugPigment.h>
#include <klocale.h>
KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0;
KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::m_instance = 0;
KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::m_instance = 0;
KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::m_instance = 0;
KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0;
KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0;
KoSegmentGradient::KoSegmentGradient(const QString& file)
: KoAbstractGradient(file)
{
}
KoSegmentGradient::~KoSegmentGradient()
{
for (int i = 0; i < m_segments.count(); i++) {
delete m_segments[i];
m_segments[i] = 0;
}
}
KoSegmentGradient::KoSegmentGradient(const KoSegmentGradient &rhs)
: KoAbstractGradient(rhs)
{
foreach(KoGradientSegment *segment, rhs.m_segments) {
pushSegment(new KoGradientSegment(*segment));
}
}
KoAbstractGradient* KoSegmentGradient::clone() const
{
return new KoSegmentGradient(*this);
}
bool KoSegmentGradient::load()
{
QFile file(filename());
if (!file.open(QIODevice::ReadOnly)) {
- kWarning() << "Can't open file " << filename();
+ warnPigment << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KoSegmentGradient::loadFromDevice(QIODevice *dev)
{
QByteArray data = dev->readAll();
QTextStream fileContent(data, QIODevice::ReadOnly);
fileContent.setAutoDetectUnicode(true);
QString header = fileContent.readLine();
if (header != "GIMP Gradient") {
return false;
}
QString nameDefinition = fileContent.readLine();
QString numSegmentsText;
if (nameDefinition.startsWith("Name: ")) {
QString nameText = nameDefinition.right(nameDefinition.length() - 6);
setName(nameText);
numSegmentsText = fileContent.readLine();
} else {
// Older format without name.
numSegmentsText = nameDefinition;
}
dbgPigment << "Loading gradient: " << name();
int numSegments;
bool ok;
numSegments = numSegmentsText.toInt(&ok);
if (!ok || numSegments < 1) {
return false;
}
dbgPigment << "Number of segments = " << numSegments;
const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8();
for (int i = 0; i < numSegments; i++) {
QString segmentText = fileContent.readLine();
QTextStream segmentFields(&segmentText);
QStringList values = segmentText.split(' ');
qreal leftOffset = values[0].toDouble();
qreal middleOffset = values[1].toDouble();
qreal rightOffset = values[2].toDouble();
qreal leftRed = values[3].toDouble();
qreal leftGreen = values[4].toDouble();
qreal leftBlue = values[5].toDouble();
qreal leftAlpha = values[6].toDouble();
qreal rightRed = values[7].toDouble();
qreal rightGreen = values[8].toDouble();
qreal rightBlue = values[9].toDouble();
qreal rightAlpha = values[10].toDouble();
int interpolationType = values[11].toInt();
int colorInterpolationType = values[12].toInt();
quint8 data[4];
data[2] = static_cast<quint8>(leftRed * 255 + 0.5);
data[1] = static_cast<quint8>(leftGreen * 255 + 0.5);
data[0] = static_cast<quint8>(leftBlue * 255 + 0.5);
data[3] = static_cast<quint8>(leftAlpha * OPACITY_OPAQUE_U8 + 0.5);
KoColor leftColor(data, rgbColorSpace);
data[2] = static_cast<quint8>(rightRed * 255 + 0.5);
data[1] = static_cast<quint8>(rightGreen * 255 + 0.5);
data[0] = static_cast<quint8>(rightBlue * 255 + 0.5);
data[3] = static_cast<quint8>(rightAlpha * OPACITY_OPAQUE_U8 + 0.5);
KoColor rightColor(data, rgbColorSpace);
KoGradientSegment *segment = new KoGradientSegment(interpolationType, colorInterpolationType, leftOffset, middleOffset, rightOffset, leftColor, rightColor);
Q_CHECK_PTR(segment);
if (!segment -> isValid()) {
delete segment;
return false;
}
m_segments.push_back(segment);
}
if (!m_segments.isEmpty()) {
updatePreview();
setValid(true);
return true;
} else {
return false;
}
}
bool KoSegmentGradient::save()
{
QFile file(filename());
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
saveToDevice(&file);
file.close();
return true;
}
bool KoSegmentGradient::saveToDevice(QIODevice *dev) const
{
QTextStream fileContent(dev);
fileContent << "GIMP Gradient\n";
fileContent << "Name: " << name() << "\n";
fileContent << m_segments.count() << "\n";
foreach(KoGradientSegment* segment, m_segments) {
fileContent << QString::number(segment->startOffset(), 'f') << " " << QString::number(segment->middleOffset(), 'f') << " "
<< QString::number(segment->endOffset(), 'f') << " ";
QColor startColor = segment->startColor().toQColor();
QColor endColor = segment->endColor().toQColor();
fileContent << QString::number(startColor.redF(), 'f') << " " << QString::number(startColor.greenF(), 'f') << " "
<< QString::number(startColor.blueF(), 'f') << " " << QString::number(startColor.alphaF(), 'f') << " ";
fileContent << QString::number(endColor.redF(), 'f') << " " << QString::number(endColor.greenF(), 'f') << " "
<< QString::number(endColor.blueF(), 'f') << " " << QString::number(endColor.alphaF(), 'f') << " ";
fileContent << (int)segment->interpolation() << " " << (int)segment->colorInterpolation() << "\n";
}
KoResource::saveToDevice(dev);
return true;
}
KoGradientSegment *KoSegmentGradient::segmentAt(qreal t) const
{
Q_ASSERT(t >= 0 || t <= 1);
Q_ASSERT(!m_segments.empty());
for (QList<KoGradientSegment *>::const_iterator it = m_segments.begin(); it != m_segments.end(); ++it) {
if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) {
return *it;
}
}
return 0;
}
void KoSegmentGradient::colorAt(KoColor& dst, qreal t) const
{
const KoGradientSegment *segment = segmentAt(t);
Q_ASSERT(segment != 0);
if (segment) {
segment->colorAt(dst, t);
}
}
QGradient* KoSegmentGradient::toQGradient() const
{
QGradient* gradient = new QLinearGradient();
QColor color;
foreach(KoGradientSegment* segment, m_segments) {
segment->startColor().toQColor(&color);
gradient->setColorAt(segment->startOffset() , color);
segment->endColor().toQColor(&color);
gradient->setColorAt(segment->endOffset() , color);
}
return gradient;
}
QString KoSegmentGradient::defaultFileExtension() const
{
return QString(".ggr");
}
KoGradientSegment::KoGradientSegment(int interpolationType, int colorInterpolationType, qreal startOffset, qreal middleOffset, qreal endOffset, const KoColor& startColor, const KoColor& endColor)
{
m_interpolator = 0;
switch (interpolationType) {
case INTERP_LINEAR:
m_interpolator = LinearInterpolationStrategy::instance();
break;
case INTERP_CURVED:
m_interpolator = CurvedInterpolationStrategy::instance();
break;
case INTERP_SINE:
m_interpolator = SineInterpolationStrategy::instance();
break;
case INTERP_SPHERE_INCREASING:
m_interpolator = SphereIncreasingInterpolationStrategy::instance();
break;
case INTERP_SPHERE_DECREASING:
m_interpolator = SphereDecreasingInterpolationStrategy::instance();
break;
}
m_colorInterpolator = 0;
switch (colorInterpolationType) {
case COLOR_INTERP_RGB:
m_colorInterpolator = RGBColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CCW:
m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CW:
m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
break;
}
if (startOffset < DBL_EPSILON) {
m_startOffset = 0;
} else if (startOffset > 1 - DBL_EPSILON) {
m_startOffset = 1;
} else {
m_startOffset = startOffset;
}
if (middleOffset < m_startOffset + DBL_EPSILON) {
m_middleOffset = m_startOffset;
} else if (middleOffset > 1 - DBL_EPSILON) {
m_middleOffset = 1;
} else {
m_middleOffset = middleOffset;
}
if (endOffset < m_middleOffset + DBL_EPSILON) {
m_endOffset = m_middleOffset;
} else if (endOffset > 1 - DBL_EPSILON) {
m_endOffset = 1;
} else {
m_endOffset = endOffset;
}
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
m_startColor = startColor;
m_endColor = endColor;
}
const KoColor& KoGradientSegment::startColor() const
{
return m_startColor;
}
const KoColor& KoGradientSegment::endColor() const
{
return m_endColor;
}
qreal KoGradientSegment::startOffset() const
{
return m_startOffset;
}
qreal KoGradientSegment::middleOffset() const
{
return m_middleOffset;
}
qreal KoGradientSegment::endOffset() const
{
return m_endOffset;
}
void KoGradientSegment::setStartOffset(qreal t)
{
m_startOffset = t;
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
void KoGradientSegment::setMiddleOffset(qreal t)
{
m_middleOffset = t;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
void KoGradientSegment::setEndOffset(qreal t)
{
m_endOffset = t;
m_length = m_endOffset - m_startOffset;
if (m_length < DBL_EPSILON) {
m_middleT = 0.5;
} else {
m_middleT = (m_middleOffset - m_startOffset) / m_length;
}
}
int KoGradientSegment::interpolation() const
{
return m_interpolator->type();
}
void KoGradientSegment::setInterpolation(int interpolationType)
{
switch (interpolationType) {
case INTERP_LINEAR:
m_interpolator = LinearInterpolationStrategy::instance();
break;
case INTERP_CURVED:
m_interpolator = CurvedInterpolationStrategy::instance();
break;
case INTERP_SINE:
m_interpolator = SineInterpolationStrategy::instance();
break;
case INTERP_SPHERE_INCREASING:
m_interpolator = SphereIncreasingInterpolationStrategy::instance();
break;
case INTERP_SPHERE_DECREASING:
m_interpolator = SphereDecreasingInterpolationStrategy::instance();
break;
}
}
int KoGradientSegment::colorInterpolation() const
{
return m_colorInterpolator->type();
}
void KoGradientSegment::setColorInterpolation(int colorInterpolationType)
{
switch (colorInterpolationType) {
case COLOR_INTERP_RGB:
m_colorInterpolator = RGBColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CCW:
m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
break;
case COLOR_INTERP_HSV_CW:
m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
break;
}
}
void KoGradientSegment::colorAt(KoColor& dst, qreal t) const
{
Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON);
qreal segmentT;
if (m_length < DBL_EPSILON) {
segmentT = 0.5;
} else {
segmentT = (t - m_startOffset) / m_length;
}
qreal colorT = m_interpolator->valueAt(segmentT, m_middleT);
m_colorInterpolator->colorAt(dst, colorT, m_startColor, m_endColor);
}
bool KoGradientSegment::isValid() const
{
if (m_interpolator == 0 || m_colorInterpolator == 0)
return false;
return true;
}
KoGradientSegment::RGBColorInterpolationStrategy::RGBColorInterpolationStrategy()
: m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()), buffer(m_colorSpace), m_start(m_colorSpace), m_end(m_colorSpace)
{
}
KoGradientSegment::RGBColorInterpolationStrategy *KoGradientSegment::RGBColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new RGBColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::RGBColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const
{
m_start.fromKoColor(start);
m_end.fromKoColor(end);
const quint8 *colors[2];
colors[0] = start.data();
colors[1] = end.data();
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((1.0 - t) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
m_colorSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
dst.fromKoColor(buffer);
}
KoGradientSegment::HSVCWColorInterpolationStrategy::HSVCWColorInterpolationStrategy()
: m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KoGradientSegment::HSVCWColorInterpolationStrategy *KoGradientSegment::HSVCWColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new HSVCWColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::HSVCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const
{
QColor sc;
QColor ec;
start.toQColor(&sc);
end.toQColor(&ec);
int s = static_cast<int>(sc.saturation() + t * (ec.saturation() - sc.saturation()) + 0.5);
int v = static_cast<int>(sc.value() + t * (ec.value() - sc.value()) + 0.5);
int h;
if (ec.hue() < sc.hue()) {
h = static_cast<int>(ec.hue() + (1 - t) * (sc.hue() - ec.hue()) + 0.5);
} else {
h = static_cast<int>(ec.hue() + (1 - t) * (360 - ec.hue() + sc.hue()) + 0.5);
if (h > 359) {
h -= 360;
}
}
// XXX: added an explicit cast. Is this correct?
quint8 opacity = static_cast<quint8>(sc.alpha() + t * (ec.alpha() - sc.alpha()));
QColor result;
result.setHsv(h, s, v);
result.setAlpha(opacity);
dst.fromQColor(result);
}
KoGradientSegment::HSVCCWColorInterpolationStrategy::HSVCCWColorInterpolationStrategy() :
m_colorSpace(KoColorSpaceRegistry::instance()->rgb8())
{
}
KoGradientSegment::HSVCCWColorInterpolationStrategy *KoGradientSegment::HSVCCWColorInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new HSVCCWColorInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
void KoGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(KoColor& dst, qreal t, const KoColor& start, const KoColor& end) const
{
QColor sc;
QColor se;
start.toQColor(&sc);
end.toQColor(&se);
int s = static_cast<int>(sc.saturation() + t * (se.saturation() - sc.saturation()) + 0.5);
int v = static_cast<int>(sc.value() + t * (se.value() - sc.value()) + 0.5);
int h;
if (sc.hue() < se.hue()) {
h = static_cast<int>(sc.hue() + t * (se.hue() - sc.hue()) + 0.5);
} else {
h = static_cast<int>(sc.hue() + t * (360 - sc.hue() + se.hue()) + 0.5);
if (h > 359) {
h -= 360;
}
}
// XXX: Added an explicit static cast
quint8 opacity = static_cast<quint8>(sc.alpha() + t * (se.alpha() - sc.alpha()));
QColor result;
result.setHsv(h, s, v);
result.setAlpha(opacity);
dst.fromQColor(result);
}
KoGradientSegment::LinearInterpolationStrategy *KoGradientSegment::LinearInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new LinearInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::LinearInterpolationStrategy::calcValueAt(qreal t, qreal middle)
{
Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
qreal value = 0;
if (t <= middle) {
if (middle < DBL_EPSILON) {
value = 0;
} else {
value = (t / middle) * 0.5;
}
} else {
if (middle > 1 - DBL_EPSILON) {
value = 1;
} else {
value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
}
}
return value;
}
qreal KoGradientSegment::LinearInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
return calcValueAt(t, middle);
}
KoGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy()
{
m_logHalf = log(0.5);
}
KoGradientSegment::CurvedInterpolationStrategy *KoGradientSegment::CurvedInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new CurvedInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::CurvedInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
qreal value = 0;
if (middle < DBL_EPSILON) {
middle = DBL_EPSILON;
}
value = pow(t, m_logHalf / log(middle));
return value;
}
KoGradientSegment::SineInterpolationStrategy *KoGradientSegment::SineInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SineInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SineInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
qreal value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
return value;
}
KoGradientSegment::SphereIncreasingInterpolationStrategy *KoGradientSegment::SphereIncreasingInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SphereIncreasingInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
qreal value = sqrt(1 - lt * lt);
return value;
}
KoGradientSegment::SphereDecreasingInterpolationStrategy *KoGradientSegment::SphereDecreasingInterpolationStrategy::instance()
{
if (m_instance == 0) {
m_instance = new SphereDecreasingInterpolationStrategy();
Q_CHECK_PTR(m_instance);
}
return m_instance;
}
qreal KoGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(qreal t, qreal middle) const
{
qreal lt = LinearInterpolationStrategy::calcValueAt(t, middle);
qreal value = 1 - sqrt(1 - lt * lt);
return value;
}
void KoSegmentGradient::createSegment(int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, const QColor & left, const QColor & right)
{
pushSegment(new KoGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, KoColor(left, colorSpace()), KoColor(right, colorSpace())));
}
const QList<double> KoSegmentGradient::getHandlePositions() const
{
QList<double> handlePositions;
handlePositions.push_back(m_segments[0]->startOffset());
for (int i = 0; i < m_segments.count(); i++) {
handlePositions.push_back(m_segments[i]->endOffset());
}
return handlePositions;
}
const QList<double> KoSegmentGradient::getMiddleHandlePositions() const
{
QList<double> middleHandlePositions;
for (int i = 0; i < m_segments.count(); i++) {
middleHandlePositions.push_back(m_segments[i]->middleOffset());
}
return middleHandlePositions;
}
void KoSegmentGradient::moveSegmentStartOffset(KoGradientSegment* segment, double t)
{
QList<KoGradientSegment*>::iterator it = qFind(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
if (it == m_segments.begin()) {
segment->setStartOffset(0.0);
return;
}
KoGradientSegment* previousSegment = (*(it - 1));
if (t > segment->startOffset()) {
if (t > segment->middleOffset())
t = segment->middleOffset();
} else {
if (t < previousSegment->middleOffset())
t = previousSegment->middleOffset();
}
previousSegment->setEndOffset(t);
segment->setStartOffset(t);
}
}
void KoSegmentGradient::moveSegmentEndOffset(KoGradientSegment* segment, double t)
{
QList<KoGradientSegment*>::iterator it = qFind(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
if (it + 1 == m_segments.end()) {
segment->setEndOffset(1.0);
return;
}
KoGradientSegment* followingSegment = (*(it + 1));
if (t < segment->endOffset()) {
if (t < segment->middleOffset())
t = segment->middleOffset();
} else {
if (t > followingSegment->middleOffset())
t = followingSegment->middleOffset();
}
followingSegment->setStartOffset(t);
segment->setEndOffset(t);
}
}
void KoSegmentGradient::moveSegmentMiddleOffset(KoGradientSegment* segment, double t)
{
if (segment) {
if (t > segment->endOffset())
segment->setMiddleOffset(segment->endOffset());
else if (t < segment->startOffset())
segment->setMiddleOffset(segment->startOffset());
else
segment->setMiddleOffset(t);
}
}
void KoSegmentGradient::splitSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
QList<KoGradientSegment*>::iterator it = qFind(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
KoColor midleoffsetColor(segment->endColor().colorSpace());
segment->colorAt(midleoffsetColor, segment->middleOffset());
KoGradientSegment* newSegment = new KoGradientSegment(
segment->interpolation(), segment->colorInterpolation(),
segment ->startOffset(),
(segment->middleOffset() - segment->startOffset()) / 2 + segment->startOffset(),
segment->middleOffset(),
segment->startColor(),
midleoffsetColor);
m_segments.insert(it, newSegment);
segment->setStartColor(midleoffsetColor);
segment->setStartOffset(segment->middleOffset());
segment->setMiddleOffset((segment->endOffset() - segment->startOffset()) / 2 + segment->startOffset());
}
}
void KoSegmentGradient::duplicateSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
QList<KoGradientSegment*>::iterator it = qFind(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
double middlePostionPercentage = (segment->middleOffset() - segment->startOffset()) / segment->length();
double center = segment->startOffset() + segment->length() / 2;
KoGradientSegment* newSegment = new KoGradientSegment(
segment->interpolation(), segment->colorInterpolation(),
segment ->startOffset(),
segment->length() / 2 * middlePostionPercentage + segment->startOffset(),
center, segment->startColor(),
segment->endColor());
m_segments.insert(it, newSegment);
segment->setStartOffset(center);
segment->setMiddleOffset(segment->length() * middlePostionPercentage + segment->startOffset());
}
}
void KoSegmentGradient::mirrorSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
KoColor tmpColor = segment->startColor();
segment->setStartColor(segment->endColor());
segment->setEndColor(tmpColor);
segment->setMiddleOffset(segment->endOffset() - (segment->middleOffset() - segment->startOffset()));
if (segment->interpolation() == INTERP_SPHERE_INCREASING)
segment->setInterpolation(INTERP_SPHERE_DECREASING);
else if (segment->interpolation() == INTERP_SPHERE_DECREASING)
segment->setInterpolation(INTERP_SPHERE_INCREASING);
if (segment->colorInterpolation() == COLOR_INTERP_HSV_CW)
segment->setColorInterpolation(COLOR_INTERP_HSV_CCW);
else if (segment->colorInterpolation() == COLOR_INTERP_HSV_CCW)
segment->setColorInterpolation(COLOR_INTERP_HSV_CW);
}
KoGradientSegment* KoSegmentGradient::removeSegment(KoGradientSegment* segment)
{
Q_ASSERT(segment != 0);
if (m_segments.count() < 2)
return 0;
QList<KoGradientSegment*>::iterator it = qFind(m_segments.begin(), m_segments.end(), segment);
if (it != m_segments.end()) {
double middlePostionPercentage;
KoGradientSegment* nextSegment;
if (it == m_segments.begin()) {
nextSegment = (*(it + 1));
middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
nextSegment->setStartOffset(segment->startOffset());
nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset());
} else {
nextSegment = (*(it - 1));
middlePostionPercentage = (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length();
nextSegment->setEndOffset(segment->endOffset());
nextSegment->setMiddleOffset(middlePostionPercentage * nextSegment->length() + nextSegment->startOffset());
}
delete segment;
m_segments.erase(it);
return nextSegment;
}
return 0;
}
bool KoSegmentGradient::removeSegmentPossible() const
{
if (m_segments.count() < 2)
return false;
return true;
}
const QList<KoGradientSegment *>& KoSegmentGradient::segments() const
{
return m_segments;
}
diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp
index 64136776709..87bf8733db6 100644
--- a/libs/pigment/resources/KoStopGradient.cpp
+++ b/libs/pigment/resources/KoStopGradient.cpp
@@ -1,657 +1,657 @@
/*
Copyright (C) 2005 Tim Beaulen <tbscope@gmail.org>
Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "KoStopGradient.h"
#include <cfloat>
#include <QColor>
#include <QFile>
#include <QDomDocument>
#include <QBuffer>
#include <klocale.h>
#include <DebugPigment.h>
#include "KoColorSpaceRegistry.h"
#include "KoMixColorsOp.h"
#include <math.h>
#include <KoColorModelStandardIds.h>
KoStopGradient::KoStopGradient(const QString& filename)
: KoAbstractGradient(filename)
{
}
KoStopGradient::~KoStopGradient()
{
}
KoAbstractGradient* KoStopGradient::clone() const
{
return new KoStopGradient(*this);
}
bool KoStopGradient::load()
{
QFile f(filename());
if (!f.open(QIODevice::ReadOnly)) {
- kWarning() << "Can't open file " << filename();
+ warnPigment << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&f);
f.close();
return res;
}
bool KoStopGradient::loadFromDevice(QIODevice *dev)
{
QString strExt;
const int result = filename().lastIndexOf('.');
if (result >= 0) {
strExt = filename().mid(result).toLower();
}
QByteArray ba = dev->readAll();
QBuffer buf(&ba);
if (strExt == ".kgr") {
loadKarbonGradient(&buf);
}
else if (strExt == ".svg") {
loadSvgGradient(&buf);
}
if (m_stops.count() >= 2) {
setValid(true);
}
updatePreview();
return true;
}
bool KoStopGradient::save()
{
QFile fileOut(filename());
if (! fileOut.open(QIODevice::WriteOnly))
return false;
bool retval = saveToDevice(&fileOut);
fileOut.close();
return retval;
}
QGradient* KoStopGradient::toQGradient() const
{
QGradient* gradient;
switch (type()) {
case QGradient::LinearGradient: {
gradient = new QLinearGradient(m_start, m_stop);
break;
}
case QGradient::RadialGradient: {
QPointF diff = m_stop - m_start;
qreal radius = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
gradient = new QRadialGradient(m_start, radius, m_focalPoint);
break;
}
case QGradient::ConicalGradient: {
qreal angle = atan2(m_start.y(), m_start.x()) * 180.0 / M_PI;
if (angle < 0.0)
angle += 360.0;
gradient = new QConicalGradient(m_start, angle);
break;
}
default:
return 0;
}
QColor color;
for (QList<KoGradientStop>::const_iterator i = m_stops.begin(); i != m_stops.end(); ++i) {
i->second.toQColor(&color);
gradient->setColorAt(i->first , color);
}
return gradient;
}
void KoStopGradient::colorAt(KoColor& dst, qreal t) const
{
if (! m_stops.count())
return;
if (t <= m_stops.first().first || m_stops.count() == 1) {
// we have only one stop or t is before the first stop
// -> use the color of the first stop
dst.fromKoColor(m_stops.first().second);
} else if (t >= m_stops.last().first) {
// t is after the last stop
// -> use the color of the last stop
dst.fromKoColor(m_stops.last().second);
} else {
// we have at least two color stops
// -> find the two stops which frame our t
QList<KoGradientStop>::const_iterator stop = m_stops.begin();
QList<KoGradientStop>::const_iterator lastStop = m_stops.end();
// we already checked the first stop, so we start at the second
for (++stop; stop != lastStop; ++stop) {
// we break at the stop which is just after our t
if (stop->first > t)
break;
}
if ( !(*buffer.colorSpace() == *colorSpace())) {
buffer = KoColor(colorSpace());
}
const KoGradientStop& leftStop = *(stop - 1);
const KoGradientStop& rightStop = *(stop);
const quint8 *colors[2];
colors[0] = leftStop.second.data();
colors[1] = rightStop.second.data();
qreal localT;
qreal stopDistance = rightStop.first - leftStop.first;
if (stopDistance < DBL_EPSILON) {
localT = 0.5;
} else {
localT = (t - leftStop.first) / stopDistance;
}
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((1.0 - localT) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
dst.fromKoColor(buffer);
}
}
KoStopGradient * KoStopGradient::fromQGradient(QGradient * gradient)
{
if (! gradient)
return 0;
KoStopGradient * newGradient = new KoStopGradient("");
newGradient->setType(gradient->type());
newGradient->setSpread(gradient->spread());
switch (gradient->type()) {
case QGradient::LinearGradient: {
QLinearGradient * g = static_cast<QLinearGradient*>(gradient);
newGradient->m_start = g->start();
newGradient->m_stop = g->finalStop();
newGradient->m_focalPoint = g->start();
break;
}
case QGradient::RadialGradient: {
QRadialGradient * g = static_cast<QRadialGradient*>(gradient);
newGradient->m_start = g->center();
newGradient->m_stop = g->center() + QPointF(g->radius(), 0);
newGradient->m_focalPoint = g->focalPoint();
break;
}
case QGradient::ConicalGradient: {
QConicalGradient * g = static_cast<QConicalGradient*>(gradient);
qreal radian = g->angle() * M_PI / 180.0;
newGradient->m_start = g->center();
newGradient->m_stop = QPointF(100.0 * cos(radian), 100.0 * sin(radian));
newGradient->m_focalPoint = g->center();
break;
}
default:
delete newGradient;
return 0;
}
foreach(const QGradientStop & stop, gradient->stops()) {
KoColor color(newGradient->colorSpace());
color.fromQColor(stop.second);
newGradient->m_stops.append(KoGradientStop(stop.first, color));
}
return newGradient;
}
void KoStopGradient::setStops(QList< KoGradientStop > stops)
{
m_stops.clear();
KoColor color;
foreach(const KoGradientStop & stop, stops) {
color = stop.second;
color.convertTo(colorSpace());
m_stops.append(KoGradientStop(stop.first, color));
}
updatePreview();
}
QList<KoGradientStop> KoStopGradient::stops() const
{
return m_stops;
}
void KoStopGradient::loadKarbonGradient(QIODevice *file)
{
QDomDocument doc;
if (!(doc.setContent(file))) {
file->close();
setValid(false);
return;
}
QDomElement e;
QDomNode n = doc.documentElement().firstChild();
if (!n.isNull()) {
e = n.toElement();
if (!e.isNull() && e.tagName() == "GRADIENT") {
parseKarbonGradient(e);
}
}
}
void KoStopGradient::loadSvgGradient(QIODevice *file)
{
QDomDocument doc;
if (!(doc.setContent(file)))
file->close();
else {
for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (e.isNull()) continue;
if (e.tagName() == "linearGradient" || e.tagName() == "radialGradient") {
parseSvgGradient(e);
return;
}
// Inkscape gradients are in another defs
if (e.tagName() == "defs") {
for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) {
QDomElement defelement = defnode.toElement();
if (defelement.isNull()) continue;
if (defelement.tagName() == "linearGradient" || defelement.tagName() == "radialGradient") {
parseSvgGradient(defelement);
return;
}
}
}
}
}
}
void KoStopGradient::parseKarbonGradient(const QDomElement& element)
{
m_start = QPointF(element.attribute("originX", "0.0").toDouble(), element.attribute("originY", "0.0").toDouble());
m_focalPoint = QPointF(element.attribute("focalX", "0.0").toDouble(), element.attribute("focalY", "0.0").toDouble());
m_stop = QPointF(element.attribute("vectorX", "0.0").toDouble(), element.attribute("vectorY", "0.0").toDouble());
setType((QGradient::Type)element.attribute("type", 0).toInt());
setSpread((QGradient::Spread)element.attribute("repeatMethod", 0).toInt());
m_stops.clear();
qreal color1, color2, color3, color4, opacity;
KoColor color;
// load stops
QDomNodeList list = element.childNodes();
for (int i = 0; i < list.count(); ++i) {
if (list.item(i).isElement()) {
QDomElement colorstop = list.item(i).toElement();
if (colorstop.tagName() == "COLORSTOP") {
QDomElement e = colorstop.firstChild().toElement();
opacity = e.attribute("opacity", "1.0").toFloat();
QColor tmpColor;
const KoColorSpace* stopColorSpace;
switch (e.attribute("colorSpace").toUShort()) {
case 1: // cmyk
color1 = e.attribute("v1", "0.0").toFloat();
color2 = e.attribute("v2", "0.0").toFloat();
color3 = e.attribute("v3", "0.0").toFloat();
color4 = e.attribute("v4", "0.0").toFloat();
stopColorSpace = KoColorSpaceRegistry::instance()->colorSpace( CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString());
if (stopColorSpace) {
quint8 data[5];
data[0] = static_cast<quint8>(color1 * 255 + 0.5);
data[1] = static_cast<quint8>(color2 * 255 + 0.5);
data[2] = static_cast<quint8>(color3 * 255 + 0.5);
data[3] = static_cast<quint8>(color4 * 255 + 0.5);
data[4] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
color.setColor(data, stopColorSpace);
} else {
// cmyk colorspace not found fallback to rgb
color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
tmpColor.setCmykF(color1, color2, color3, color4);
tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
color.fromQColor(tmpColor);
}
break;
case 2: // hsv
color1 = e.attribute("v1", "0.0").toFloat();
color2 = e.attribute("v2", "0.0").toFloat();
color3 = e.attribute("v3", "0.0").toFloat();
color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
tmpColor.setHsvF(color1, color2, color3);
tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
color.fromQColor(tmpColor);
break;
case 3: // gray
color1 = e.attribute("v1", "0.0").toFloat();
stopColorSpace = KoColorSpaceRegistry::instance()->colorSpace( GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), QString());
if (stopColorSpace) {
quint8 data[2];
data[0] = static_cast<quint8>(color1 * 255 + 0.5);
data[1] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
color.setColor(data, stopColorSpace);
} else {
// gray colorspace not found fallback to rgb
color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
tmpColor.setRgbF(color1, color1, color1);
tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
color.fromQColor(tmpColor);
}
break;
default: // rgb
color1 = e.attribute("v1", "0.0").toFloat();
color2 = e.attribute("v2", "0.0").toFloat();
color3 = e.attribute("v3", "0.0").toFloat();
stopColorSpace = KoColorSpaceRegistry::instance()->rgb8();
quint8 data[4];
data[2] = static_cast<quint8>(color1 * 255 + 0.5);
data[1] = static_cast<quint8>(color2 * 255 + 0.5);
data[0] = static_cast<quint8>(color3 * 255 + 0.5);
data[3] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
color.setColor(data, stopColorSpace);
}
qreal offset = colorstop.attribute("ramppoint", "0.0").toFloat();
// midpoint = colorstop.attribute("midpoint", "0.5").toFloat();
m_stops.append(KoGradientStop(offset, color));
}
}
}
}
void KoStopGradient::parseSvgGradient(const QDomElement& element)
{
m_stops.clear();
setSpread(QGradient::PadSpread);
/*QString href = e.attribute( "xlink:href" ).mid( 1 );
if( !href.isEmpty() )
{
}*/
setName(element.attribute("id", i18n("SVG Gradient")));
const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8();
bool bbox = element.attribute("gradientUnits") != "userSpaceOnUse";
if (element.tagName() == "linearGradient") {
if (bbox) {
QString s;
s = element.attribute("x1", "0%");
qreal xOrigin;
if (s.endsWith('%'))
xOrigin = s.remove('%').toDouble();
else
xOrigin = s.toDouble() * 100.0;
s = element.attribute("y1", "0%");
qreal yOrigin;
if (s.endsWith('%'))
yOrigin = s.remove('%').toDouble();
else
yOrigin = s.toDouble() * 100.0;
s = element.attribute("x2", "100%");
qreal xVector;
if (s.endsWith('%'))
xVector = s.remove('%').toDouble();
else
xVector = s.toDouble() * 100.0;
s = element.attribute("y2", "0%");
qreal yVector;
if (s.endsWith('%'))
yVector = s.remove('%').toDouble();
else
yVector = s.toDouble() * 100.0;
m_start = QPointF(xOrigin, yOrigin);
m_stop = QPointF(xVector, yVector);
} else {
m_start = QPointF(element.attribute("x1").toDouble(), element.attribute("y1").toDouble());
m_stop = QPointF(element.attribute("x2").toDouble(), element.attribute("y2").toDouble());
}
setType(QGradient::LinearGradient);
} else {
if (bbox) {
QString s;
s = element.attribute("cx", "50%");
qreal xOrigin;
if (s.endsWith('%'))
xOrigin = s.remove('%').toDouble();
else
xOrigin = s.toDouble() * 100.0;
s = element.attribute("cy", "50%");
qreal yOrigin;
if (s.endsWith('%'))
yOrigin = s.remove('%').toDouble();
else
yOrigin = s.toDouble() * 100.0;
s = element.attribute("cx", "50%");
qreal xVector;
if (s.endsWith('%'))
xVector = s.remove('%').toDouble();
else
xVector = s.toDouble() * 100.0;
s = element.attribute("r", "50%");
if (s.endsWith('%'))
xVector += s.remove('%').toDouble();
else
xVector += s.toDouble() * 100.0;
s = element.attribute("cy", "50%");
qreal yVector;
if (s.endsWith('%'))
yVector = s.remove('%').toDouble();
else
yVector = s.toDouble() * 100.0;
s = element.attribute("fx", "50%");
qreal xFocal;
if (s.endsWith('%'))
xFocal = s.remove('%').toDouble();
else
xFocal = s.toDouble() * 100.0;
s = element.attribute("fy", "50%");
qreal yFocal;
if (s.endsWith('%'))
yFocal = s.remove('%').toDouble();
else
yFocal = s.toDouble() * 100.0;
m_start = QPointF(xOrigin, yOrigin);
m_stop = QPointF(xVector, yVector);
m_focalPoint = QPointF(xFocal, yFocal);
} else {
m_start = QPointF(element.attribute("cx").toDouble(), element.attribute("cy").toDouble());
m_stop = QPointF(element.attribute("cx").toDouble() + element.attribute("r").toDouble(),
element.attribute("cy").toDouble());
m_focalPoint = QPointF(element.attribute("fx").toDouble(), element.attribute("fy").toDouble());
}
setType(QGradient::RadialGradient);
}
// handle spread method
QString spreadMethod = element.attribute("spreadMethod");
if (!spreadMethod.isEmpty()) {
if (spreadMethod == "reflect")
setSpread(QGradient::ReflectSpread);
else if (spreadMethod == "repeat")
setSpread(QGradient::RepeatSpread);
}
for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement colorstop = n.toElement();
if (colorstop.tagName() == "stop") {
qreal opacity = 0.0;
QColor c;
float off;
QString temp = colorstop.attribute("offset");
if (temp.contains('%')) {
temp = temp.left(temp.length() - 1);
off = temp.toFloat() / 100.0;
} else
off = temp.toFloat();
if (!colorstop.attribute("stop-color").isEmpty())
parseSvgColor(c, colorstop.attribute("stop-color"));
else {
// try style attr
QString style = colorstop.attribute("style").simplified();
QStringList substyles = style.split(';', QString::SkipEmptyParts);
foreach(const QString & s, substyles) {
QStringList substyle = s.split(':');
QString command = substyle[0].trimmed();
QString params = substyle[1].trimmed();
if (command == "stop-color")
parseSvgColor(c, params);
if (command == "stop-opacity")
opacity = params.toDouble();
}
}
if (!colorstop.attribute("stop-opacity").isEmpty())
opacity = colorstop.attribute("stop-opacity").toDouble();
KoColor color(rgbColorSpace);
color.fromQColor(c);
color.setOpacity(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
//According to the SVG spec each gradient offset has to be equal to or greater than the previous one
//if not it needs to be adjusted to be equal
if (m_stops.count() > 0 && m_stops.last().first >= off) {
off = m_stops.last().first;
}
m_stops.append(KoGradientStop(off, color));
}
}
}
void KoStopGradient::parseSvgColor(QColor &color, const QString &s)
{
if (s.startsWith("rgb(")) {
QString parse = s.trimmed();
QStringList colors = parse.split(',');
QString r = colors[0].right((colors[0].length() - 4));
QString g = colors[1];
QString b = colors[2].left((colors[2].length() - 1));
if (r.contains('%')) {
r = r.left(r.length() - 1);
r = QString::number(int((qreal(255 * r.toDouble()) / 100.0)));
}
if (g.contains('%')) {
g = g.left(g.length() - 1);
g = QString::number(int((qreal(255 * g.toDouble()) / 100.0)));
}
if (b.contains('%')) {
b = b.left(b.length() - 1);
b = QString::number(int((qreal(255 * b.toDouble()) / 100.0)));
}
color = QColor(r.toInt(), g.toInt(), b.toInt());
} else {
QString rgbColor = s.trimmed();
QColor c;
if (rgbColor.startsWith('#'))
c.setNamedColor(rgbColor);
else {
c = QColor(rgbColor);
}
color = c;
}
}
QString KoStopGradient::defaultFileExtension() const
{
return QString(".svg");
}
bool KoStopGradient::saveToDevice(QIODevice *dev) const
{
QTextStream stream(dev);
const QString spreadMethod[3] = {
QString("spreadMethod=\"pad\" "),
QString("spreadMethod=\"reflect\" "),
QString("spreadMethod=\"repeat\" ")
};
const QString indent = " ";
stream << "<svg>" << endl;
stream << indent;
stream << "<linearGradient id=\"" << name() << "\" ";
stream << "gradientUnits=\"objectBoundingBox\" ";
stream << spreadMethod[spread()];
stream << ">" << endl;
QColor color;
// color stops
foreach(const KoGradientStop & stop, m_stops) {
stop.second.toQColor(&color);
stream << indent << indent;
stream << "<stop stop-color=\"";
stream << color.name();
stream << "\" offset=\"" << QString().setNum(stop.first);
stream << "\" stop-opacity=\"" << static_cast<float>(color.alpha()) / 255.0f << "\"" << " />" << endl;
}
stream << indent;
stream << "</linearGradient>" << endl;
stream << "</svg>" << endl;
KoResource::saveToDevice(dev);
return true;
}
diff --git a/libs/pigment/tests/CCSGraph.cpp b/libs/pigment/tests/CCSGraph.cpp
index ab9a1c294fd..20bcbd7f1f7 100644
--- a/libs/pigment/tests/CCSGraph.cpp
+++ b/libs/pigment/tests/CCSGraph.cpp
@@ -1,115 +1,115 @@
/*
* Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QFile>
#include <QProcess>
#include <QTemporaryFile>
#include <k4aboutdata.h>
#include <kcmdlineargs.h>
#include <kapplication.h>
#include <DebugPigment.h>
#include "KoColorSpaceRegistry.h"
#include "KoColorConversionSystem.h"
#include <iostream>
int main(int argc, char** argv)
{
K4AboutData aboutData("CCSGraph",
"CCSGraph",
ki18n("CCSGraph"),
"1.0",
ki18n("Output the graph of color conversion of pigment's Color Conversion"),
K4AboutData::License_LGPL,
ki18n("(c) 2007 Cyrille Berger"),
KLocalizedString(),
"www.calligra.org",
"submit@bugs.kde.org");
KCmdLineArgs::init(argc, argv, &aboutData);
// Initialize the list of options
KCmdLineOptions options;
options.add("graphs", ki18n("return the list of available graphs"));
options.add("graph <type>", ki18n("specify the type of graph (see --graphs to get the full list, the default is full)"), "full");
options.add("src-key <key>", ki18n("specify the key of the source color space"), "");
options.add("dst-key <key>", ki18n("specify the key of the destination color space"), "");
options.add("output <type>", ki18n("specify the output (can be ps or dot, the default is ps)"), "ps");
options.add("+outputfile", ki18n("name of the output file"));
KCmdLineArgs::addCmdLineOptions(options);
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
if (args->isSet("graphs")) {
// Don't change those lines to use dbgPigment derivatives, they need to be outputed
// to stdout not stderr.
std::cout << "full : show all the connection on the graph" << std::endl;
std::cout << "bestpath : show the best path for a given transformation" << std::endl;
exit(EXIT_SUCCESS);
}
QString graphType = args->getOption("graph");
QString outputType = args->getOption("output");
if (args->count() != 1) {
- kError() << "No output file name specified";
+ errorPigment << "No output file name specified";
args->usage();
exit(EXIT_FAILURE);
}
QString outputFileName = args->arg(0);
// Generate the graph
KApplication app;
QString dot;
if (graphType == "full") {
dot = KoColorSpaceRegistry::instance()->colorConversionSystem()->toDot();
} else if (graphType == "bestpath") {
QString srcKey = args->getOption("src-key");
QString dstKey = args->getOption("dst-key");
if (srcKey.isEmpty() || dstKey.isEmpty()) {
- kError() << "src-key and dst-key must be specified for the graph bestpath";
+ errorPigment << "src-key and dst-key must be specified for the graph bestpath";
exit(EXIT_FAILURE);
} else {
dot = KoColorSpaceRegistry::instance()->colorConversionSystem()->bestPathToDot(srcKey, dstKey);
}
} else {
- kError() << "Unknow graph type : " << graphType.toLatin1();
+ errorPigment << "Unknow graph type : " << graphType.toLatin1();
exit(EXIT_FAILURE);
}
if (outputType == "dot") {
QFile file(outputFileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
exit(EXIT_FAILURE);
QTextStream out(&file);
out << dot;
} else if (outputType == "ps" || outputType == "svg") {
QTemporaryFile file;
if (!file.open()) {
exit(EXIT_FAILURE);
}
QTextStream out(&file);
out << dot;
QString cmd = QString("dot -T%1 %2 -o %3").arg(outputType).arg(file.fileName()).arg(outputFileName);
file.close();
if (QProcess::execute(cmd) != 0) {
- kError() << "An error has occurred when executing : '" << cmd << "' the most likely cause is that 'dot' command is missing, and that you should install graphviz (from http://www.graphiz.org)";
+ errorPigment << "An error has occurred when executing : '" << cmd << "' the most likely cause is that 'dot' command is missing, and that you should install graphviz (from http://www.graphiz.org)";
}
} else {
- kError() << "Unknow output type : " << outputType;
+ errorPigment << "Unknow output type : " << outputType;
exit(EXIT_FAILURE);
}
}
diff --git a/libs/widgets/KoRuler.cpp b/libs/widgets/KoRuler.cpp
index 1afba66e25d..765e82752a6 100644
--- a/libs/widgets/KoRuler.cpp
+++ b/libs/widgets/KoRuler.cpp
@@ -1,1340 +1,1337 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
Copyright (C) 2006 Peter Simonsson <peter.simonsson@gmail.com>
Copyright (C) 2007 C. Boemann <cbo@boemann.dk>
Copyright (C) 2007-2008 Jan Hambrecht <jaham@gmx.net>
Copyright (C) 2007 Thomas Zander <zander@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KoRuler.h"
#include "KoRuler_p.h"
#include <KoToolBase.h>
#include <KoToolManager.h>
#include <klocale.h>
#include <kdebug.h>
#include <kglobalsettings.h>
#include <QPainter>
#include <QResizeEvent>
#include <QMenu>
#include <QMouseEvent>
#include <KoViewConverter.h>
// the distance in pixels of a mouse position considered outside the rule
static const int OutsideRulerThreshold = 20;
//
static const int fullStepMarkerLength = 6;
static const int halfStepMarkerLength = 6;
static const int quarterStepMarkerLength = 3;
static const int measurementTextAboveBelowMargin = 1;
void RulerTabChooser::mousePressEvent(QMouseEvent *)
{
if (! m_showTabs) {
return;
}
switch(m_type) {
case QTextOption::LeftTab:
m_type = QTextOption::RightTab;
break;
case QTextOption::RightTab:
m_type = QTextOption::CenterTab;
break;
case QTextOption::CenterTab:
m_type = QTextOption::DelimiterTab;
break;
case QTextOption::DelimiterTab:
m_type = QTextOption::LeftTab;
break;
}
update();
}
void RulerTabChooser::paintEvent(QPaintEvent *)
{
if (! m_showTabs) {
return;
}
QPainter painter(this);
QPolygonF polygon;
painter.setPen(palette().color(QPalette::Text));
painter.setBrush(palette().color(QPalette::Text));
painter.setRenderHint( QPainter::Antialiasing );
qreal x= width()/2;
painter.translate(0,-height()/2+5);
switch (m_type) {
case QTextOption::LeftTab:
polygon << QPointF(x+0.5, height() - 8.5)
<< QPointF(x+6.5, height() - 2.5)
<< QPointF(x+0.5, height() - 2.5);
painter.drawPolygon(polygon);
break;
case QTextOption::RightTab:
polygon << QPointF(x+0.5, height() - 8.5)
<< QPointF(x-5.5, height() - 2.5)
<< QPointF(x+0.5, height() - 2.5);
painter.drawPolygon(polygon);
break;
case QTextOption::CenterTab:
polygon << QPointF(x+0.5, height() - 8.5)
<< QPointF(x-5.5, height() - 2.5)
<< QPointF(x+6.5, height() - 2.5);
painter.drawPolygon(polygon);
break;
case QTextOption::DelimiterTab:
polygon << QPointF(x-5.5, height() - 2.5)
<< QPointF(x+6.5, height() - 2.5);
painter.drawPolyline(polygon);
polygon << QPointF(x+0.5, height() - 2.5)
<< QPointF(x+0.5, height() - 8.5);
painter.drawPolyline(polygon);
break;
default:
break;
}
}
static int compareTabs(KoRuler::Tab &tab1, KoRuler::Tab &tab2)
{
return tab1.position < tab2.position;
}
QRectF HorizontalPaintingStrategy::drawBackground(const KoRulerPrivate *d, QPainter &painter)
{
lengthInPixel = d->viewConverter->documentToViewX(d->rulerLength);
QRectF rectangle;
rectangle.setX(qMax(0, d->offset));
rectangle.setY(0);
rectangle.setWidth(qMin(qreal(d->ruler->width() - 1.0 - rectangle.x()),
(d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
rectangle.setHeight(d->ruler->height() - 1);
QRectF activeRangeRectangle;
activeRangeRectangle.setX(qMax(rectangle.x() + 1,
d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()) + d->offset));
activeRangeRectangle.setY(rectangle.y() + 1);
activeRangeRectangle.setRight(qMin(rectangle.right() - 1,
d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()) + d->offset));
activeRangeRectangle.setHeight(rectangle.height() - 2);
painter.setPen(d->ruler->palette().color(QPalette::Mid));
painter.drawRect(rectangle);
if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
painter.fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
if(d->showSelectionBorders) {
// Draw first selection border
if(d->firstSelectionBorder > 0) {
qreal border = d->viewConverter->documentToViewX(d->firstSelectionBorder) + d->offset;
painter.drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
}
// Draw second selection border
if(d->secondSelectionBorder > 0) {
qreal border = d->viewConverter->documentToViewX(d->secondSelectionBorder) + d->offset;
painter.drawLine(QPointF(border, rectangle.y() + 1), QPointF(border, rectangle.bottom() - 1));
}
}
return rectangle;
}
void HorizontalPaintingStrategy::drawTabs(const KoRulerPrivate *d, QPainter &painter)
{
if (! d->showTabs)
return;
QPolygonF polygon;
const QColor tabColor = d->ruler->palette().color(QPalette::Text);
painter.setPen(tabColor);
painter.setBrush(tabColor);
painter.setRenderHint( QPainter::Antialiasing );
qreal position = -10000;
foreach (const KoRuler::Tab & t, d->tabs) {
qreal x;
if (d->rightToLeft) {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
- (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
} else {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
+ (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
}
position = qMax(position, t.position);
polygon.clear();
switch (t.type) {
case QTextOption::LeftTab:
polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
<< QPointF(x+6.5, d->ruler->height() - 0.5)
<< QPointF(x+0.5, d->ruler->height() - 0.5);
painter.drawPolygon(polygon);
break;
case QTextOption::RightTab:
polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
<< QPointF(x-5.5, d->ruler->height() - 0.5)
<< QPointF(x+0.5, d->ruler->height() - 0.5);
painter.drawPolygon(polygon);
break;
case QTextOption::CenterTab:
polygon << QPointF(x+0.5, d->ruler->height() - 6.5)
<< QPointF(x-5.5, d->ruler->height() - 0.5)
<< QPointF(x+6.5, d->ruler->height() - 0.5);
painter.drawPolygon(polygon);
break;
case QTextOption::DelimiterTab:
polygon << QPointF(x-5.5, d->ruler->height() - 0.5)
<< QPointF(x+6.5, d->ruler->height() - 0.5);
painter.drawPolyline(polygon);
polygon << QPointF(x+0.5, d->ruler->height() - 0.5)
<< QPointF(x+0.5, d->ruler->height() - 6.5);
painter.drawPolyline(polygon);
break;
default:
break;
}
}
// and also draw the regular interval tab that are non editable
if (d->tabDistance > 0.0) {
// first possible position
position = qMax(position, d->relativeTabs ? 0 : d->paragraphIndent);
if (position < 0) {
position = int(position / d->tabDistance) * d->tabDistance;
} else {
position = (int(position / d->tabDistance) + 1) * d->tabDistance;
}
while (position < d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart()
- d->endIndent) {
qreal x;
if (d->rightToLeft) {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
- (d->relativeTabs ? d->paragraphIndent : 0) - position) + d->offset;
} else {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
+ (d->relativeTabs ? d->paragraphIndent : 0) + position) + d->offset;
}
polygon.clear();
polygon << QPointF(x+0.5, d->ruler->height() - 3.5)
<< QPointF(x+4.5, d->ruler->height() - 0.5)
<< QPointF(x+0.5, d->ruler->height() - 0.5);
painter.drawPolygon(polygon);
position += d->tabDistance;
}
}
}
void HorizontalPaintingStrategy::drawMeasurements(const KoRulerPrivate *d, QPainter &painter, const QRectF &rectangle)
{
qreal numberStep = d->numberStepForUnit(); // number step in unit
// QRectF activeRangeRectangle;
int numberStepPixel = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(numberStep)));
// const bool adjustMillimeters = (d->unit.type() == KoUnit::Millimeter);
const QFont font = KGlobalSettings::smallestReadableFont();
const QFontMetrics fontMetrics(font);
painter.setFont(font);
if (numberStepPixel == 0 || numberStep == 0)
return;
// Calc the longest text length
int textLength = 0;
for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
int number = qRound((i / numberStepPixel) * numberStep);
textLength = qMax(textLength, fontMetrics.width(QString::number(number)));
}
textLength += 4; // Add some padding
// Change number step so all digits fits
while(textLength > numberStepPixel) {
numberStepPixel += numberStepPixel;
numberStep += numberStep;
}
int start=0;
// Calc the first number step
if(d->offset < 0)
start = qAbs(d->offset);
// make a little hack so rulers shows correctly inversed number aligned
const qreal lengthInUnit = d->unit.toUserValue(d->rulerLength);
const qreal hackyLength = lengthInUnit - fmod(lengthInUnit, numberStep);
if(d->rightToLeft) {
start -= int(d->viewConverter->documentToViewX(fmod(d->rulerLength,
d->unit.fromUserValue(numberStep))));
}
int stepCount = (start / numberStepPixel) + 1;
int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
int pos = 0;
const QPen numberPen(d->ruler->palette().color(QPalette::Text));
const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
painter.setPen(markerPen);
if(d->offset > 0)
painter.translate(d->offset, 0);
const int len = qRound(rectangle.width()) + start;
int nextStep = qRound(d->viewConverter->documentToViewX(
d->unit.fromUserValue(numberStep * stepCount)));
int nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
int nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
for(int i = start; i < len; ++i) {
pos = i - start;
if(i == nextStep) {
if(pos != 0)
painter.drawLine(QPointF(pos, rectangle.bottom()-1),
QPointF(pos, rectangle.bottom() - fullStepMarkerLength));
int number = qRound(stepCount * numberStep);
QString numberText = QString::number(number);
int x = pos;
if (d->rightToLeft) { // this is done in a hacky way with the fine tuning done above
numberText = QString::number(hackyLength - stepCount * numberStep);
}
painter.setPen(numberPen);
painter.drawText(QPointF(x-fontMetrics.width(numberText)/2.0,
rectangle.bottom() -fullStepMarkerLength -measurementTextAboveBelowMargin),
numberText);
painter.setPen(markerPen);
++stepCount;
nextStep = qRound(d->viewConverter->documentToViewX(
d->unit.fromUserValue(numberStep * stepCount)));
++halfStepCount;
nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
}
else if(i == nextHalfStep) {
if(pos != 0)
painter.drawLine(QPointF(pos, rectangle.bottom()-1),
QPointF(pos, rectangle.bottom() - halfStepMarkerLength));
++halfStepCount;
nextHalfStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
}
else if(i == nextQuarterStep) {
if(pos != 0)
painter.drawLine(QPointF(pos, rectangle.bottom()-1),
QPointF(pos, rectangle.bottom() - quarterStepMarkerLength));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewX(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
}
}
// Draw the mouse indicator
const int mouseCoord = d->mouseCoordinate - start;
if (d->selected == KoRulerPrivate::None || d->selected == KoRulerPrivate::HotSpot) {
const qreal top = rectangle.y() + 1;
const qreal bottom = rectangle.bottom() -1;
if (d->selected == KoRulerPrivate::None && d->showMousePosition && mouseCoord > 0 && mouseCoord < rectangle.width() )
painter.drawLine(QPointF(mouseCoord, top), QPointF(mouseCoord, bottom));
foreach (const KoRulerPrivate::HotSpotData & hp, d->hotspots) {
const qreal x = d->viewConverter->documentToViewX(hp.position) + d->offset;
painter.drawLine(QPointF(x, top), QPointF(x, bottom));
}
}
}
void HorizontalPaintingStrategy::drawIndents(const KoRulerPrivate *d, QPainter &painter)
{
QPolygonF polygon;
painter.setBrush(d->ruler->palette().brush(QPalette::Base));
painter.setRenderHint( QPainter::Antialiasing );
qreal x;
// Draw first line start indent
if (d->rightToLeft)
x = d->effectiveActiveRangeEnd() - d->firstLineIndent - d->paragraphIndent;
else
x = d->effectiveActiveRangeStart() + d->firstLineIndent + d->paragraphIndent;
// convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
polygon << QPointF(x+6.5, 0.5)
<< QPointF(x+0.5, 8.5)
<< QPointF(x-5.5, 0.5)
<< QPointF(x+5.5, 0.5);
painter.drawPolygon(polygon);
// draw the hanging indent.
if (d->rightToLeft)
x = d->effectiveActiveRangeStart() + d->endIndent;
else
x = d->effectiveActiveRangeStart() + d->paragraphIndent;
// convert and use the +0.5 to go to nearest integer so that the 0.5 added below ensures sharp lines
x = int(d->viewConverter->documentToViewX(x) + d->offset + 0.5);
const int bottom = d->ruler->height();
polygon.clear();
polygon << QPointF(x+6.5, bottom - 0.5)
<< QPointF(x+0.5, bottom - 8.5)
<< QPointF(x-5.5, bottom - 0.5)
<< QPointF(x+5.5, bottom - 0.5);
painter.drawPolygon(polygon);
// Draw end-indent or paragraph indent if mode is rightToLeft
qreal diff;
if (d->rightToLeft)
diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
- d->paragraphIndent) + d->offset - x;
else
diff = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd() - d->endIndent)
+ d->offset - x;
polygon.translate(diff, 0);
painter.drawPolygon(polygon);
}
QSize HorizontalPaintingStrategy::sizeHint()
{
// assumes that digits for the number only use glyphs which do not go below the baseline
const QFontMetrics fm(KGlobalSettings::smallestReadableFont());
const int digitsHeight = fm.ascent() + 1; // +1 for baseline
const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
return QSize(0, minimum);
}
QRectF VerticalPaintingStrategy::drawBackground(const KoRulerPrivate *d, QPainter &painter)
{
lengthInPixel = d->viewConverter->documentToViewY(d->rulerLength);
QRectF rectangle;
rectangle.setX(0);
rectangle.setY(qMax(0, d->offset));
rectangle.setWidth(d->ruler->width() - 1.0);
rectangle.setHeight(qMin(qreal(d->ruler->height() - 1.0 - rectangle.y()),
(d->offset >= 0) ? lengthInPixel : lengthInPixel + d->offset));
QRectF activeRangeRectangle;
activeRangeRectangle.setX(rectangle.x() + 1);
activeRangeRectangle.setY(qMax(rectangle.y() + 1,
d->viewConverter->documentToViewY(d->effectiveActiveRangeStart()) + d->offset));
activeRangeRectangle.setWidth(rectangle.width() - 2);
activeRangeRectangle.setBottom(qMin(rectangle.bottom() - 1,
d->viewConverter->documentToViewY(d->effectiveActiveRangeEnd()) + d->offset));
painter.setPen(d->ruler->palette().color(QPalette::Mid));
painter.drawRect(rectangle);
if(d->effectiveActiveRangeStart() != d->effectiveActiveRangeEnd())
painter.fillRect(activeRangeRectangle, d->ruler->palette().brush(QPalette::Base));
if(d->showSelectionBorders) {
// Draw first selection border
if(d->firstSelectionBorder > 0) {
qreal border = d->viewConverter->documentToViewY(d->firstSelectionBorder) + d->offset;
painter.drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
}
// Draw second selection border
if(d->secondSelectionBorder > 0) {
qreal border = d->viewConverter->documentToViewY(d->secondSelectionBorder) + d->offset;
painter.drawLine(QPointF(rectangle.x() + 1, border), QPointF(rectangle.right() - 1, border));
}
}
return rectangle;
}
void VerticalPaintingStrategy::drawMeasurements(const KoRulerPrivate *d, QPainter &painter, const QRectF &rectangle)
{
qreal numberStep = d->numberStepForUnit(); // number step in unit
int numberStepPixel = qRound(d->viewConverter->documentToViewY( d->unit.fromUserValue(numberStep)));
if (numberStepPixel <= 0)
return;
const QFont font = KGlobalSettings::smallestReadableFont();
const QFontMetrics fontMetrics(font);
painter.setFont(font);
// Calc the longest text length
int textLength = 0;
for(int i = 0; i < lengthInPixel; i += numberStepPixel) {
int number = qRound((i / numberStepPixel) * numberStep);
textLength = qMax(textLength, fontMetrics.width(QString::number(number)));
}
textLength += 4; // Add some padding
if (numberStepPixel == 0 || numberStep == 0)
return;
// Change number step so all digits will fit
while(textLength > numberStepPixel) {
numberStepPixel += numberStepPixel;
numberStep += numberStep;
}
// Calc the first number step
const int start = d->offset < 0 ? qAbs(d->offset) : 0;
// make a little hack so rulers shows correctly inversed number aligned
int stepCount = (start / numberStepPixel) + 1;
int halfStepCount = (start / qRound(numberStepPixel * 0.5)) + 1;
int quarterStepCount = (start / qRound(numberStepPixel * 0.25)) + 1;
const QPen numberPen(d->ruler->palette().color(QPalette::Text));
const QPen markerPen(d->ruler->palette().color(QPalette::Inactive, QPalette::Text));
painter.setPen(markerPen);
if(d->offset > 0)
painter.translate(0, d->offset);
const int len = qRound(rectangle.height()) + start;
int nextStep = qRound(d->viewConverter->documentToViewY(
d->unit.fromUserValue(numberStep * stepCount)));
int nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
int nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
int pos = 0;
for(int i = start; i < len; ++i) {
pos = i - start;
if(i == nextStep) {
painter.save();
painter.translate(rectangle.right()-fullStepMarkerLength, pos);
if(pos != 0)
painter.drawLine(QPointF(0, 0), QPointF(fullStepMarkerLength-1, 0));
painter.rotate(-90);
int number = qRound(stepCount * numberStep);
QString numberText = QString::number(number);
painter.setPen(numberPen);
painter.drawText(QPointF(-fontMetrics.width(numberText) / 2.0, -measurementTextAboveBelowMargin), numberText);
painter.restore();
++stepCount;
nextStep = qRound(d->viewConverter->documentToViewY(
d->unit.fromUserValue(numberStep * stepCount)));
++halfStepCount;
nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
} else if(i == nextHalfStep) {
if(pos != 0)
painter.drawLine(QPointF(rectangle.right() - halfStepMarkerLength, pos),
QPointF(rectangle.right() - 1, pos));
++halfStepCount;
nextHalfStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.5 * halfStepCount)));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
} else if(i == nextQuarterStep) {
if(pos != 0)
painter.drawLine(QPointF(rectangle.right() - quarterStepMarkerLength, pos),
QPointF(rectangle.right() - 1, pos));
++quarterStepCount;
nextQuarterStep = qRound(d->viewConverter->documentToViewY(d->unit.fromUserValue(
numberStep * 0.25 * quarterStepCount)));
}
}
// Draw the mouse indicator
const int mouseCoord = d->mouseCoordinate - start;
if (d->selected == KoRulerPrivate::None || d->selected == KoRulerPrivate::HotSpot) {
const qreal left = rectangle.left() + 1;
const qreal right = rectangle.right() -1;
if (d->selected == KoRulerPrivate::None && d->showMousePosition && mouseCoord > 0 && mouseCoord < rectangle.height() )
painter.drawLine(QPointF(left, mouseCoord), QPointF(right, mouseCoord));
foreach (const KoRulerPrivate::HotSpotData & hp, d->hotspots) {
const qreal y = d->viewConverter->documentToViewY(hp.position) + d->offset;
painter.drawLine(QPointF(left, y), QPointF(right, y));
}
}
}
QSize VerticalPaintingStrategy::sizeHint()
{
// assumes that digits for the number only use glyphs which do not go below the baseline
const QFontMetrics fm(KGlobalSettings::smallestReadableFont());
const int digitsHeight = fm.ascent() + 1; // +1 for baseline
const int minimum = digitsHeight + fullStepMarkerLength + 2*measurementTextAboveBelowMargin;
return QSize(minimum, 0);
}
void HorizontalDistancesPaintingStrategy::drawDistanceLine(const KoRulerPrivate *d, QPainter &painter, const qreal start, const qreal end)
{
// Don't draw too short lines
if (qMax(start, end) - qMin(start, end) < 1)
return;
painter.save();
painter.translate(d->offset, d->ruler->height() / 2);
painter.setPen(d->ruler->palette().color(QPalette::Text));
painter.setBrush(d->ruler->palette().color(QPalette::Text));
QLineF line(QPointF(d->viewConverter->documentToViewX(start), 0),
QPointF(d->viewConverter->documentToViewX(end), 0));
QPointF midPoint = line.pointAt(0.5);
// Draw the label text
const QFont font = KGlobalSettings::smallestReadableFont();
const QFontMetrics fontMetrics(font);
QString label = d->unit.toUserStringValue(
d->viewConverter->viewToDocumentX(line.length())) + ' ' + d->unit.symbol();
QPointF labelPosition = QPointF(midPoint.x() - fontMetrics.width(label)/2,
midPoint.y() + fontMetrics.ascent()/2);
painter.setFont(font);
painter.drawText(labelPosition, label);
// Draw the arrow lines
qreal arrowLength = (line.length() - fontMetrics.width(label)) / 2 - 2;
arrowLength = qMax(qreal(0.0), arrowLength);
QLineF startArrow(line.p1(), line.pointAt(arrowLength / line.length()));
QLineF endArrow(line.p2(), line.pointAt(1.0 - arrowLength / line.length()));
painter.drawLine(startArrow);
painter.drawLine(endArrow);
// Draw the arrow heads
QPolygonF arrowHead;
arrowHead << line.p1() << QPointF(line.x1()+3, line.y1()-3)
<< QPointF(line.x1()+3, line.y1()+3);
painter.drawPolygon(arrowHead);
arrowHead.clear();
arrowHead << line.p2() << QPointF(line.x2()-3, line.y2()-3)
<< QPointF(line.x2()-3, line.y2()+3);
painter.drawPolygon(arrowHead);
painter.restore();
}
void HorizontalDistancesPaintingStrategy::drawMeasurements(const KoRulerPrivate *d, QPainter &painter, const QRectF&)
{
QList<qreal> points;
points << 0.0;
points << d->effectiveActiveRangeStart() + d->paragraphIndent + d->firstLineIndent;
points << d->effectiveActiveRangeStart() + d->paragraphIndent;
points << d->effectiveActiveRangeEnd() - d->endIndent;
points << d->effectiveActiveRangeStart();
points << d->effectiveActiveRangeEnd();
points << d->rulerLength;
qSort(points.begin(), points.end());
QListIterator<qreal> i(points);
i.next();
while (i.hasNext() && i.hasPrevious()) {
drawDistanceLine(d, painter, i.peekPrevious(), i.peekNext());
i.next();
}
}
KoRulerPrivate::KoRulerPrivate(KoRuler *parent, const KoViewConverter *vc, Qt::Orientation o)
: unit(KoUnit(KoUnit::Point)),
orientation(o),
viewConverter(vc),
offset(0),
rulerLength(0),
activeRangeStart(0),
activeRangeEnd(0),
activeOverrideRangeStart(0),
activeOverrideRangeEnd(0),
mouseCoordinate(-1),
showMousePosition(0),
showSelectionBorders(false),
firstSelectionBorder(0),
secondSelectionBorder(0),
showIndents(false),
firstLineIndent(0),
paragraphIndent(0),
endIndent(0),
showTabs(false),
relativeTabs(false),
tabMoved(false),
originalIndex(-1),
currentIndex(0),
rightToLeft(false),
selected(None),
selectOffset(0),
tabChooser(0),
normalPaintingStrategy(o == Qt::Horizontal ?
(PaintingStrategy*)new HorizontalPaintingStrategy() : (PaintingStrategy*)new VerticalPaintingStrategy()),
distancesPaintingStrategy((PaintingStrategy*)new HorizontalDistancesPaintingStrategy()),
paintingStrategy(normalPaintingStrategy),
ruler(parent)
{
}
KoRulerPrivate::~KoRulerPrivate()
{
delete normalPaintingStrategy;
delete distancesPaintingStrategy;
}
qreal KoRulerPrivate::numberStepForUnit() const
{
switch(unit.type()) {
case KoUnit::Inch:
case KoUnit::Centimeter:
case KoUnit::Decimeter:
case KoUnit::Millimeter:
return 1.0;
case KoUnit::Pica:
case KoUnit::Cicero:
return 10.0;
case KoUnit::Point:
default:
return 100.0;
}
}
qreal KoRulerPrivate::doSnapping(const qreal value) const
{
qreal numberStep = unit.fromUserValue(numberStepForUnit()/4.0);
return numberStep * qRound(value / numberStep);
}
KoRulerPrivate::Selection KoRulerPrivate::selectionAtPosition(const QPoint & pos, int *selectOffset )
{
const int height = ruler->height();
if (rightToLeft) {
int x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - firstLineIndent - paragraphIndent) + offset);
if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() < height / 2) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::FirstLineIndent;
}
x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - paragraphIndent) + offset);
if (pos.x() >= x - 8 && pos.x() <= x +8 && pos.y() > height / 2) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::ParagraphIndent;
}
x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + endIndent) + offset);
if (pos.x() >= x - 8 && pos.x() <= x + 8) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::EndIndent;
}
}
else {
int x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + firstLineIndent + paragraphIndent) + offset);
if (pos.x() >= x -8 && pos.x() <= x + 8 && pos.y() < height / 2) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::FirstLineIndent;
}
x = int(viewConverter->documentToViewX(effectiveActiveRangeStart() + paragraphIndent) + offset);
if (pos.x() >= x - 8 && pos.x() <= x + 8 && pos.y() > height/2) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::ParagraphIndent;
}
x = int(viewConverter->documentToViewX(effectiveActiveRangeEnd() - endIndent) + offset);
if (pos.x() >= x - 8 && pos.x() <= x + 8) {
if (selectOffset)
*selectOffset = x - pos.x();
return KoRulerPrivate::EndIndent;
}
}
return KoRulerPrivate::None;
}
int KoRulerPrivate::hotSpotIndex(const QPoint & pos)
{
for(int counter = 0; counter < hotspots.count(); counter++) {
bool hit;
if (orientation == Qt::Horizontal)
hit = qAbs(viewConverter->documentToViewX(hotspots[counter].position) - pos.x() + offset) < 3;
else
hit = qAbs(viewConverter->documentToViewY(hotspots[counter].position) - pos.y() + offset) < 3;
if (hit)
return counter;
}
return -1;
}
qreal KoRulerPrivate::effectiveActiveRangeStart() const
{
if (activeOverrideRangeStart != activeOverrideRangeEnd) {
return activeOverrideRangeStart;
} else {
return activeRangeStart;
}
}
qreal KoRulerPrivate::effectiveActiveRangeEnd() const
{
if (activeOverrideRangeStart != activeOverrideRangeEnd) {
return activeOverrideRangeEnd;
} else {
return activeRangeEnd;
}
}
void KoRulerPrivate::emitTabChanged()
{
KoRuler::Tab tab;
if (currentIndex >= 0)
tab = tabs[currentIndex];
emit ruler->tabChanged(originalIndex, currentIndex >= 0 ? &tab : 0);
}
KoRuler::KoRuler(QWidget* parent, Qt::Orientation orientation, const KoViewConverter* viewConverter)
: QWidget(parent)
, d( new KoRulerPrivate( this, viewConverter, orientation) )
{
setMouseTracking( true );
}
KoRuler::~KoRuler()
{
delete d;
}
KoUnit KoRuler::unit() const
{
return d->unit;
}
void KoRuler::setUnit(const KoUnit &unit)
{
d->unit = unit;
update();
}
qreal KoRuler::rulerLength() const
{
return d->rulerLength;
}
Qt::Orientation KoRuler::orientation() const
{
return d->orientation;
}
void KoRuler::setOffset(int offset)
{
d->offset = offset;
update();
}
void KoRuler::setRulerLength(qreal length)
{
d->rulerLength = length;
update();
}
void KoRuler::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setClipRegion(event->region());
painter.save();
QRectF rectangle = d->paintingStrategy->drawBackground(d, painter);
painter.restore();
painter.save();
d->paintingStrategy->drawMeasurements(d, painter, rectangle);
painter.restore();
if (d->showIndents) {
painter.save();
d->paintingStrategy->drawIndents(d, painter);
painter.restore();
}
d->paintingStrategy->drawTabs(d, painter);
}
QSize KoRuler::minimumSizeHint() const
{
return d->paintingStrategy->sizeHint();
}
QSize KoRuler::sizeHint() const
{
return d->paintingStrategy->sizeHint();
}
void KoRuler::setActiveRange(qreal start, qreal end)
{
d->activeRangeStart = start;
d->activeRangeEnd = end;
update();
}
void KoRuler::setOverrideActiveRange(qreal start, qreal end)
{
d->activeOverrideRangeStart = start;
d->activeOverrideRangeEnd = end;
update();
}
void KoRuler::updateMouseCoordinate(int coordinate)
{
if(d->mouseCoordinate == coordinate)
return;
d->mouseCoordinate = coordinate;
update();
}
void KoRuler::setShowMousePosition(bool show)
{
d->showMousePosition = show;
update();
}
void KoRuler::setRightToLeft(bool isRightToLeft)
{
d->rightToLeft = isRightToLeft;
update();
}
void KoRuler::setShowIndents(bool show)
{
d->showIndents = show;
update();
}
void KoRuler::setFirstLineIndent(qreal indent)
{
d->firstLineIndent = indent;
if (d->showIndents) {
update();
}
}
void KoRuler::setParagraphIndent(qreal indent)
{
d->paragraphIndent = indent;
if (d->showIndents) {
update();
}
}
void KoRuler::setEndIndent(qreal indent)
{
d->endIndent = indent;
if (d->showIndents) {
update();
}
}
qreal KoRuler::firstLineIndent() const
{
return d->firstLineIndent;
}
qreal KoRuler::paragraphIndent() const
{
return d->paragraphIndent;
}
qreal KoRuler::endIndent() const
{
return d->endIndent;
}
QWidget *KoRuler::tabChooser()
{
if ((d->tabChooser == 0) && (d->orientation == Qt::Horizontal)) {
d->tabChooser = new RulerTabChooser(parentWidget());
d->tabChooser->setShowTabs(d->showTabs);
}
return d->tabChooser;
}
void KoRuler::setShowSelectionBorders(bool show)
{
d->showSelectionBorders = show;
update();
}
void KoRuler::updateSelectionBorders(qreal first, qreal second)
{
d->firstSelectionBorder = first;
d->secondSelectionBorder = second;
if(d->showSelectionBorders)
update();
}
void KoRuler::setShowTabs(bool show)
{
if (d->showTabs == show) {
return;
}
d->showTabs = show;
if (d->tabChooser) {
d->tabChooser->setShowTabs(show);
}
update();
}
void KoRuler::setRelativeTabs(bool relative)
{
d->relativeTabs = relative;
if (d->showTabs) {
update();
}
}
void KoRuler::updateTabs(const QList<KoRuler::Tab> &tabs, qreal tabDistance)
{
d->tabs = tabs;
d->tabDistance = tabDistance;
if (d->showTabs) {
update();
}
}
QList<KoRuler::Tab> KoRuler::tabs() const
{
QList<Tab> answer = d->tabs;
qSort(answer.begin(), answer.end(), compareTabs);
return answer;
}
void KoRuler::setPopupActionList(const QList<QAction*> &popupActionList)
{
d->popupActions = popupActionList;
}
QList<QAction*> KoRuler::popupActionList() const
{
return d->popupActions;
}
void KoRuler::mousePressEvent ( QMouseEvent* ev )
{
d->tabMoved = false;
d->selected = KoRulerPrivate::None;
if (ev->button() == Qt::RightButton && !d->popupActions.isEmpty())
QMenu::exec(d->popupActions, ev->globalPos());
if (ev->button() != Qt::LeftButton) {
ev->ignore();
return;
}
QPoint pos = ev->pos();
if (d->showTabs) {
int i = 0;
int x;
foreach (const Tab & t, d->tabs) {
if (d->rightToLeft) {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeEnd()
- (d->relativeTabs ? d->paragraphIndent : 0) - t.position) + d->offset;
} else {
x = d->viewConverter->documentToViewX(d->effectiveActiveRangeStart()
+ (d->relativeTabs ? d->paragraphIndent : 0) + t.position) + d->offset;
}
if (pos.x() >= x-6 && pos.x() <= x+6) {
d->selected = KoRulerPrivate::Tab;
d->selectOffset = x - pos.x();
d->currentIndex = i;
break;
}
i++;
}
d->originalIndex = d->currentIndex;
}
if (d->selected == KoRulerPrivate::None)
d->selected = d->selectionAtPosition(ev->pos(), &d->selectOffset);
if (d->selected == KoRulerPrivate::None) {
int hotSpotIndex = d->hotSpotIndex(ev->pos());
if (hotSpotIndex >= 0) {
d->selected = KoRulerPrivate::HotSpot;
update();
}
}
if (d->showTabs && d->selected == KoRulerPrivate::None) {
// still haven't found something so let assume the user wants to add a tab
qreal tabpos;
if (d->rightToLeft) {
tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
+ d->effectiveActiveRangeEnd() + (d->relativeTabs ? d->paragraphIndent : 0);
} else {
tabpos = d->viewConverter->viewToDocumentX(pos.x() - d->offset)
- d->effectiveActiveRangeStart() - (d->relativeTabs ? d->paragraphIndent : 0);
}
Tab t = {tabpos, d->tabChooser ? d->tabChooser->type() :
d->rightToLeft ? QTextOption::RightTab :
QTextOption::LeftTab};
d->tabs.append(t);
d->selectOffset = 0;
d->selected = KoRulerPrivate::Tab;
d->currentIndex = d->tabs.count() - 1;
d->originalIndex = -1; // new!
update();
}
if (d->orientation == Qt::Horizontal && (ev->modifiers() & Qt::ShiftModifier) &&
(d->selected == KoRulerPrivate::FirstLineIndent ||
d->selected == KoRulerPrivate::ParagraphIndent ||
d->selected == KoRulerPrivate::Tab ||
d->selected == KoRulerPrivate::EndIndent))
d->paintingStrategy = d->distancesPaintingStrategy;
if (d->selected != KoRulerPrivate::None)
emit aboutToChange();
}
void KoRuler::mouseReleaseEvent ( QMouseEvent* ev )
{
ev->accept();
if (d->selected == KoRulerPrivate::Tab) {
if (d->originalIndex >= 0 && !d->tabMoved) {
int type = d->tabs[d->currentIndex].type;
type++;
if (type > 3)
type = 0;
d->tabs[d->currentIndex].type = static_cast<QTextOption::TabType> (type);
update();
}
d->emitTabChanged();
}
else if( d->selected != KoRulerPrivate::None)
emit indentsChanged(true);
else
ev->ignore();
d->paintingStrategy = d->normalPaintingStrategy;
d->selected = KoRulerPrivate::None;
}
void KoRuler::mouseMoveEvent ( QMouseEvent* ev )
{
QPoint pos = ev->pos();
qreal activeLength = d->effectiveActiveRangeEnd() - d->effectiveActiveRangeStart();
switch (d->selected) {
case KoRulerPrivate::FirstLineIndent:
if (d->rightToLeft)
d->firstLineIndent = d->effectiveActiveRangeEnd() - d->paragraphIndent -
d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
else
d->firstLineIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
- d->offset) - d->effectiveActiveRangeStart() - d->paragraphIndent;
if( ! (ev->modifiers() & Qt::ShiftModifier)) {
d->firstLineIndent = d->doSnapping(d->firstLineIndent);
d->paintingStrategy = d->normalPaintingStrategy;
} else {
if (d->orientation == Qt::Horizontal)
d->paintingStrategy = d->distancesPaintingStrategy;
}
emit indentsChanged(false);
break;
case KoRulerPrivate::ParagraphIndent:
if (d->rightToLeft)
d->paragraphIndent = d->effectiveActiveRangeEnd() -
d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
else
d->paragraphIndent = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
- d->offset) - d->effectiveActiveRangeStart();
if( ! (ev->modifiers() & Qt::ShiftModifier)) {
d->paragraphIndent = d->doSnapping(d->paragraphIndent);
d->paintingStrategy = d->normalPaintingStrategy;
} else {
if (d->orientation == Qt::Horizontal)
d->paintingStrategy = d->distancesPaintingStrategy;
}
if (d->paragraphIndent + d->endIndent > activeLength)
d->paragraphIndent = activeLength - d->endIndent;
emit indentsChanged(false);
break;
case KoRulerPrivate::EndIndent:
if (d->rightToLeft)
d->endIndent = d->viewConverter->viewToDocumentX(pos.x()
+ d->selectOffset - d->offset) - d->effectiveActiveRangeStart();
else
d->endIndent = d->effectiveActiveRangeEnd() - d->viewConverter->viewToDocumentX(pos.x()
+ d->selectOffset - d->offset);
if (!(ev->modifiers() & Qt::ShiftModifier)) {
d->endIndent = d->doSnapping(d->endIndent);
d->paintingStrategy = d->normalPaintingStrategy;
} else {
if (d->orientation == Qt::Horizontal)
d->paintingStrategy = d->distancesPaintingStrategy;
}
if (d->paragraphIndent + d->endIndent > activeLength)
d->endIndent = activeLength - d->paragraphIndent;
emit indentsChanged(false);
break;
case KoRulerPrivate::Tab:
d->tabMoved = true;
if (d->currentIndex < 0) { // tab is deleted.
if (ev->pos().y() < height()) { // reinstante it.
d->currentIndex = d->tabs.count();
d->tabs.append(d->deletedTab);
} else {
break;
}
}
if (d->rightToLeft)
d->tabs[d->currentIndex].position = d->effectiveActiveRangeEnd() -
d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset - d->offset);
else
d->tabs[d->currentIndex].position = d->viewConverter->viewToDocumentX(pos.x() + d->selectOffset
- d->offset) - d->effectiveActiveRangeStart();
if (!(ev->modifiers() & Qt::ShiftModifier))
d->tabs[d->currentIndex].position = d->doSnapping(d->tabs[d->currentIndex].position);
if (d->tabs[d->currentIndex].position < 0)
d->tabs[d->currentIndex].position = 0;
if (d->tabs[d->currentIndex].position > activeLength)
d->tabs[d->currentIndex].position = activeLength;
if (ev->pos().y() > height() + OutsideRulerThreshold ) { // moved out of the ruler, delete it.
d->deletedTab = d->tabs.takeAt(d->currentIndex);
d->currentIndex = -1;
// was that a temporary added tab?
if ( d->originalIndex == -1 )
emit guideLineCreated(d->orientation,
d->orientation == Qt::Horizontal
? d->viewConverter->viewToDocumentY(ev->pos().y())
: d->viewConverter->viewToDocumentX(ev->pos().x()));
}
d->emitTabChanged();
break;
case KoRulerPrivate::HotSpot:
qreal newPos;
if (d->orientation == Qt::Horizontal)
newPos= d->viewConverter->viewToDocumentX(pos.x() - d->offset);
else
newPos= d->viewConverter->viewToDocumentY(pos.y() - d->offset);
d->hotspots[d->currentIndex].position = newPos;
emit hotSpotChanged(d->hotspots[d->currentIndex].id, newPos);
break;
case KoRulerPrivate::None:
d->mouseCoordinate = (d->orientation == Qt::Horizontal ? pos.x() : pos.y()) - d->offset;
int hotSpotIndex = d->hotSpotIndex(pos);
if (hotSpotIndex >= 0) {
setCursor(QCursor( d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor ));
break;
}
unsetCursor();
KoRulerPrivate::Selection selection = d->selectionAtPosition(pos);
QString text;
switch(selection) {
case KoRulerPrivate::FirstLineIndent: text = i18n("First line indent"); break;
case KoRulerPrivate::ParagraphIndent: text = i18n("Left indent"); break;
case KoRulerPrivate::EndIndent: text = i18n("Right indent"); break;
case KoRulerPrivate::None:
if (ev->buttons() & Qt::LeftButton) {
if (d->orientation == Qt::Horizontal && ev->pos().y() > height() + OutsideRulerThreshold)
emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentY(ev->pos().y()));
else if (d->orientation == Qt::Vertical && ev->pos().x() > width() + OutsideRulerThreshold)
emit guideLineCreated(d->orientation, d->viewConverter->viewToDocumentX(ev->pos().x()));
}
break;
default:
break;
}
setToolTip(text);
}
update();
}
void KoRuler::clearHotSpots()
{
if (d->hotspots.isEmpty())
return;
d->hotspots.clear();
update();
}
void KoRuler::setHotSpot(qreal position, int id)
{
uint hotspotCount = d->hotspots.count();
for( uint i = 0; i < hotspotCount; ++i ) {
KoRulerPrivate::HotSpotData & hs = d->hotspots[i];
if (hs.id == id) {
hs.position = position;
update();
return;
}
}
// not there yet, then insert it.
KoRulerPrivate::HotSpotData hs;
hs.position = position;
hs.id = id;
d->hotspots.append(hs);
}
bool KoRuler::removeHotSpot(int id)
{
QList<KoRulerPrivate::HotSpotData>::Iterator iter = d->hotspots.begin();
while(iter != d->hotspots.end()) {
if (iter->id == id) {
d->hotspots.erase(iter);
update();
return true;
}
}
return false;
}
void KoRuler::createGuideToolConnection(KoCanvasBase *canvas)
{
Q_ASSERT(canvas);
KoToolBase *tool = KoToolManager::instance()->toolById(canvas, QLatin1String("GuidesTool_ID"));
- if (tool == 0) {
- kWarning(30003) << "No guides tool found, skipping connection";
- return;
- }
+ if (!tool) return; // It's perfectly fine to have no guides tool, we don't have to warn the user about it
connect(this, SIGNAL(guideLineCreated(Qt::Orientation,qreal)),
tool, SLOT(createGuideLine(Qt::Orientation,qreal)));
}
diff --git a/plan/libs/kernel/tests/AppointmentIntervalTester.h b/plan/libs/kernel/tests/AppointmentIntervalTester.h
index 1916215bef3..40565ac8a34 100644
--- a/plan/libs/kernel/tests/AppointmentIntervalTester.h
+++ b/plan/libs/kernel/tests/AppointmentIntervalTester.h
@@ -1,43 +1,43 @@
/* This file is part of the KDE project
Copyright (C) 2008 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_AppointmentIntervalTester_h
#define KPlato_AppointmentIntervalTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class AppointmentIntervalTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void interval();
void addInterval();
void addAppointment();
void addTangentIntervals();
void subtractList();
void subtractListMidnight();
};
}
#endif
diff --git a/plan/libs/kernel/tests/CalendarTester.h b/plan/libs/kernel/tests/CalendarTester.h
index 1c15c624aa2..54eb1a5a4e4 100644
--- a/plan/libs/kernel/tests/CalendarTester.h
+++ b/plan/libs/kernel/tests/CalendarTester.h
@@ -1,48 +1,48 @@
/* This file is part of the KDE project
Copyright (C) 2006-2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_CalendarTester_h
#define KPlato_CalendarTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class CalendarTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testSingleDay();
void testWeekdays();
void testCalendarWithParent();
void testTimezone();
void workIntervals();
void workIntervalsFullDays();
private:
void removeDir(const QString &dir);
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/CommandsTester.h b/plan/libs/kernel/tests/CommandsTester.h
index f658a8b6b92..3201407ed48 100644
--- a/plan/libs/kernel/tests/CommandsTester.h
+++ b/plan/libs/kernel/tests/CommandsTester.h
@@ -1,62 +1,61 @@
/* This file is part of the KDE project
Copyright (C) 2011 Pierre Stirnweiss <pstirnweiss@googlemail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef COMMANDSTESTER_H
#define COMMANDSTESTER_H
-#include <QtTest>
#include <QObject>
namespace KPlato {
class Project;
class CommandsTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testNamedCommand();
void testCalendarAddCmd();
void testCalendarRemoveCmd();
void testCalendarMoveCmd();
void testCalendarModifyNameCmd();
void testCalendarModifyParentCmd();
void testCalendarModifyTimeZoneCmd();
void testCalendarAddDayCmd();
void testCalendarRemoveDayCmd();
void testCalendarModifyDayCmd();
void testCalendarModifyStateCmd();
void testCalendarModifyTimeIntervalCmd();
void testCalendarAddTimeIntervalCmd();
void testCalendarRemoveTimeIntervalCmd();
void testCalendarModifyWeekdayCmd();
void testCalendarModifyDateCmd();
void testProjectModifyDefaultCalendarCmd();
private:
Project *m_project;
};
} // namespace KPlato
#endif // COMMANDSTESTER_H
diff --git a/plan/libs/kernel/tests/DateTimeTester.h b/plan/libs/kernel/tests/DateTimeTester.h
index 106718055c8..66af58eb1d7 100644
--- a/plan/libs/kernel/tests/DateTimeTester.h
+++ b/plan/libs/kernel/tests/DateTimeTester.h
@@ -1,50 +1,50 @@
/* This file is part of the KDE project
Copyright (C) 2006-2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_DateTimeTester_h
#define KPlato_DateTimeTester_h
-#include <QtTest>
+#include <QObject>
#include "kptdatetime.h"
namespace KPlato
{
class DateTimeTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void subtractDay();
void subtractHour();
void subtractMinute();
void subtractSecond();
void subtractMillisecond();
void addDay();
void addHour();
void addMinute();
void addSecond();
void addMillisecond();
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/DurationTester.cpp b/plan/libs/kernel/tests/DurationTester.cpp
index 64b8879a8fd..4c5fb045aef 100644
--- a/plan/libs/kernel/tests/DurationTester.cpp
+++ b/plan/libs/kernel/tests/DurationTester.cpp
@@ -1,88 +1,90 @@
/* This file is part of the KDE project
Copyright (C) 2006-2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "DurationTester.h"
#include <kptduration.h>
+#include <QTest>
+
namespace KPlato
{
void DurationTester::add() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY((d1+d1) == Duration(0, 4, 0));
}
void DurationTester::subtract() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY((d1-d1) == Duration(0, 0, 0));
QVERIFY((d2-d1) == Duration(0, 22, 0));
QVERIFY((d1-d2) == Duration::zeroDuration); // underflow, return 0
}
void DurationTester::divide() {
Duration d1(0, 2, 0);
QVERIFY((d1/2) == Duration(0, 1, 0));
}
void DurationTester::equal() {
Duration d1(0, 2, 0);
QVERIFY(d1==d1);
}
void DurationTester::lessThanOrEqual() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY(d1<=d1);
QVERIFY(d1<=d2);
}
void DurationTester::greaterThanOrEqual() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY(d1>=d1);
QVERIFY(d2>=d1);
}
void DurationTester::notEqual() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY(!(d1!=d1));
QVERIFY(d1!=d2);
QVERIFY(d2!=d1);
}
void DurationTester::greaterThan() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY(d2>d1);
QVERIFY(d1 > 1*60*60*1000);
}
void DurationTester::lessThan() {
Duration d1(0, 2, 0);
Duration d2(1, 0, 0);
QVERIFY(d1<d2);
QVERIFY(d1 < 3*60*60*1000);
}
} //namespace KPlato
QTEST_GUILESS_MAIN( KPlato::DurationTester )
diff --git a/plan/libs/kernel/tests/DurationTester.h b/plan/libs/kernel/tests/DurationTester.h
index ddab20f5496..2834243812f 100644
--- a/plan/libs/kernel/tests/DurationTester.h
+++ b/plan/libs/kernel/tests/DurationTester.h
@@ -1,46 +1,46 @@
/* This file is part of the KDE project
Copyright (C) 2006-2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_DurationTester_h
#define KPlato_DurationTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class DurationTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void add();
void subtract();
void divide();
void equal();
void lessThanOrEqual();
void greaterThanOrEqual();
void notEqual();
void greaterThan();
void lessThan();
};
}
#endif
diff --git a/plan/libs/kernel/tests/EstimateTester.cpp b/plan/libs/kernel/tests/EstimateTester.cpp
index c215d96ec92..f414d219b5f 100644
--- a/plan/libs/kernel/tests/EstimateTester.cpp
+++ b/plan/libs/kernel/tests/EstimateTester.cpp
@@ -1,272 +1,274 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "EstimateTester.h"
#include "kptduration.h"
#include "kptnode.h"
+#include <QTest>
+
namespace KPlato
{
void EstimateTester::expected() {
Estimate e1;
e1.clear();
QVERIFY( e1.expectedEstimate() == 0.0 );
QVERIFY( e1.expectedValue().milliseconds() == 0.0 );
e1.setExpectedEstimate( 1.0 );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_ms );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1 );
e1.setUnit( Duration::Unit_s );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000 );
e1.setUnit( Duration::Unit_m );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000*60 );
e1.setUnit( Duration::Unit_h );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_d );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000*60*60*24 );
e1.setUnit( Duration::Unit_w );
QVERIFY( e1.expectedEstimate() == 1.0 );
QVERIFY( e1.expectedValue().milliseconds() == 1000*60*60*24*7 );
e1.setUnit( Duration::Unit_M );
QVERIFY( e1.expectedEstimate() == 1.0 );
QCOMPARE( e1.expectedValue().milliseconds(), qint64(1000*60*60) * (24*30) );
e1.setUnit( Duration::Unit_Y );
QVERIFY( e1.expectedEstimate() == 1.0 );
QCOMPARE( e1.expectedValue().milliseconds(), qint64(1000*60*60) * (24*365) );
}
void EstimateTester::optimistic() {
Estimate e1;
e1.clear();
QVERIFY( e1.optimisticEstimate() == 0.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 0.0 );
e1.setOptimisticEstimate( 1.0 );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_ms );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1 );
e1.setUnit( Duration::Unit_s );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1000 );
e1.setUnit( Duration::Unit_m );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1000*60 );
e1.setUnit( Duration::Unit_h );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_d );
QVERIFY( e1.optimisticEstimate() == 1.0 );
QVERIFY( e1.optimisticValue().milliseconds() == 1000*60*60*24 );
}
void EstimateTester::pessimistic() {
Estimate e1;
e1.clear();
QVERIFY( e1.pessimisticEstimate() == 0.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 0.0 );
e1.setPessimisticEstimate( 1.0 );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_ms );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1 );
e1.setUnit( Duration::Unit_s );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1000 );
e1.setUnit( Duration::Unit_m );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1000*60 );
e1.setUnit( Duration::Unit_h );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1000*60*60 );
e1.setUnit( Duration::Unit_d );
QVERIFY( e1.pessimisticEstimate() == 1.0 );
QVERIFY( e1.pessimisticValue().milliseconds() == 1000*60*60*24 );
}
void EstimateTester::ratio() {
Estimate e1;
e1.clear();
e1.setExpectedEstimate( 1.0 );
e1.setOptimisticEstimate( 1.0 );
e1.setPessimisticEstimate( 1.0 );
QVERIFY( e1.pessimisticRatio() == 0 );
QVERIFY( e1.optimisticRatio() == 0 );
e1.setExpectedEstimate( 2.0 );
e1.setOptimisticEstimate( 1.0 );
e1.setPessimisticEstimate( 4.0 );
QVERIFY( e1.pessimisticRatio() == 100 );
QVERIFY( e1.optimisticRatio() == -50 );
e1.setUnit( Duration::Unit_h );
e1.setOptimisticEstimate( 0.5 );
QVERIFY( e1.pessimisticRatio() == 100 );
QVERIFY( e1.optimisticRatio() == -75 );
e1.clear();
e1.setUnit( Duration::Unit_d );
e1.setExpectedEstimate( 1.0 );
e1.setOptimisticRatio( -50 );
e1.setPessimisticRatio( 100 );
QVERIFY( e1.pessimisticEstimate() == 2.0 );
QVERIFY( e1.optimisticEstimate() == 0.5 );
QVERIFY( e1.pessimisticValue() == 1000*60*60 * 48 );
QVERIFY( e1.optimisticValue() == 1000*60*60 * 12 );
}
void EstimateTester::defaultScale() {
QList<qint64> s = Estimate::defaultScales();
QCOMPARE( s.count(), 8 );
Duration d = Estimate::scale( 1.0, Duration::Unit_Y, s );
QCOMPARE( d.milliseconds(), s[0] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_Y, s ) );
d = Estimate::scale( 1.0, Duration::Unit_M, s );
QCOMPARE( d.milliseconds(), s[1] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_M, s ) );
d = Estimate::scale( 1.0, Duration::Unit_w, s );
QCOMPARE( d.milliseconds(), s[2] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_w, s ) );
d = Estimate::scale( 1.0, Duration::Unit_d, s );
QCOMPARE( d.milliseconds(), s[3] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_d, s ) );
d = Estimate::scale( 1.0, Duration::Unit_h, s );
QCOMPARE( d.milliseconds(), s[4] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_h, s ) );
d = Estimate::scale( 1.0, Duration::Unit_m, s );
QCOMPARE( d.milliseconds(), s[5] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_m, s ) );
d = Estimate::scale( 1.0, Duration::Unit_s, s );
QCOMPARE( d.milliseconds(), s[6] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_s, s ) );
d = Estimate::scale( 1.0, Duration::Unit_ms, s );
QCOMPARE( d.milliseconds(), s[7] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_ms, s ) );
}
void EstimateTester::scale() {
StandardWorktime wt;
QList<qint64> s = wt.scales();
QCOMPARE( s.count(), 8 );
Duration d = Estimate::scale( 1.0, Duration::Unit_Y, s );
QCOMPARE( d.milliseconds(), s[0] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_Y, s ) );
d = Estimate::scale( 1.0, Duration::Unit_M, s );
QCOMPARE( d.milliseconds(), s[1] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_M, s ) );
d = Estimate::scale( 1.0, Duration::Unit_w, s );
QCOMPARE( d.milliseconds(), s[2] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_w, s ) );
d = Estimate::scale( 1.0, Duration::Unit_d, s );
QCOMPARE( d.milliseconds(), s[3] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_d, s ) );
d = Estimate::scale( 1.0, Duration::Unit_h, s );
QCOMPARE( d.milliseconds(), s[4] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_h, s ) );
d = Estimate::scale( 1.0, Duration::Unit_m, s );
QCOMPARE( d.milliseconds(), s[5] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_m, s ) );
d = Estimate::scale( 1.0, Duration::Unit_s, s );
QCOMPARE( d.milliseconds(), s[6] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_s, s ) );
d = Estimate::scale( 1.0, Duration::Unit_ms, s );
QCOMPARE( d.milliseconds(), s[7] );
QCOMPARE( 1.0, Estimate::scale( d, Duration::Unit_ms, s ) );
}
void EstimateTester::pert() {
Estimate e1;
e1.clear();
e1.setUnit( Duration::Unit_d );
e1.setExpectedEstimate( 4.0 );
e1.setOptimisticEstimate( 2.0 );
e1.setPessimisticEstimate( 8.0 );
QVERIFY( e1.deviation() == 1.0 );
QVERIFY( e1.deviation( Duration::Unit_h ) == 24.0 );
QVERIFY( e1.variance() == 1.0 );
QVERIFY( e1.variance( Duration::Unit_h ) == 24.0*24.0 );
qint64 day = 1000*60*60*24;
e1.setRisktype( Estimate::Risk_None );
QVERIFY( e1.pertExpected().milliseconds() == 4 * day );
e1.setRisktype( Estimate::Risk_Low );
QVERIFY( e1.pertExpected().milliseconds() == ((2 + 8 + (4*4))*day)/6 );
e1.setRisktype( Estimate::Risk_High );
QVERIFY( e1.pertExpected().milliseconds() == ((2 + 16 + (4*4))*day)/7 );
}
} //namespace KPlato
QTEST_GUILESS_MAIN( KPlato::EstimateTester )
diff --git a/plan/libs/kernel/tests/EstimateTester.h b/plan/libs/kernel/tests/EstimateTester.h
index d984eb4c92b..bc09d9d9daf 100644
--- a/plan/libs/kernel/tests/EstimateTester.h
+++ b/plan/libs/kernel/tests/EstimateTester.h
@@ -1,43 +1,43 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_EstimateTester_h
#define KPlato_EstimateTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class EstimateTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void expected();
void optimistic();
void pessimistic();
void ratio();
void defaultScale();
void scale();
void pert();
};
}
#endif
diff --git a/plan/libs/kernel/tests/PerformanceTester.h b/plan/libs/kernel/tests/PerformanceTester.h
index 0397a82adfa..79d9c8d62b9 100644
--- a/plan/libs/kernel/tests/PerformanceTester.h
+++ b/plan/libs/kernel/tests/PerformanceTester.h
@@ -1,67 +1,67 @@
/* This file is part of the KDE project
Copyright (C) 2010 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_PerformanceTester_h
#define KPlato_PerformanceTester_h
-#include <QtTest>
+#include <QObject>
#include "kptdatetime.h"
#include "kptproject.h"
#include "kptduration.h"
namespace KPlato
{
class Task;
class PerformanceTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();
void bcwsPrDayTask();
void bcwpPrDayTask();
void acwpPrDayTask();
void bcwsMilestone();
void bcwpMilestone();
void acwpMilestone();
void bcwsPrDayTaskMaterial();
void bcwpPrDayTaskMaterial();
void acwpPrDayTaskMaterial();
void bcwsPrDayProject();
void bcwpPrDayProject();
void acwpPrDayProject();
private:
Project *p1;
Resource *r1;
Resource *r2; // material
Resource *r3; // material
Task *s1;
Task *t1;
Task *s2;
Task *m1;
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/ProjectTester.cpp b/plan/libs/kernel/tests/ProjectTester.cpp
index d9068e052af..6a6fd5e1dfc 100644
--- a/plan/libs/kernel/tests/ProjectTester.cpp
+++ b/plan/libs/kernel/tests/ProjectTester.cpp
@@ -1,3170 +1,3171 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "ProjectTester.h"
#include "kptcommand.h"
#include "kptcalendar.h"
#include "kptdatetime.h"
#include "kptresource.h"
#include "kptnode.h"
#include "kpttask.h"
#include "kptschedule.h"
+#include <QDir>
#include <QTest>
#include <kdebug.h>
#include <kconfiggroup.h>
#include "debug.cpp"
namespace QTest
{
template<>
char *toString(const KPlato::DateTime &dt)
{
return toString( dt.toString() );
}
}
namespace KPlato
{
void ProjectTester::initTestCase()
{
QVERIFY( m_tmp.exists() );
QFile f;
f.setFileName( m_tmp.name() + QLatin1String( "zone.tab" ) );
f.open(QIODevice::WriteOnly);
QTextStream fStream(&f);
fStream << "DE +5230+01322 Europe/Berlin\n";
f.close();
QDir dir(m_tmp.name());
QVERIFY(dir.mkdir("Europe"));
QFile::copy(QString::fromLatin1(KDESRCDIR) + QLatin1String("/Berlin"), m_tmp.name() + QLatin1String("Europe/Berlin"));
// NOTE: QTEST_KDEMAIN_CORE puts the config file in QDir::homePath() + "/.kde-unit-test"
// and hence, this is common to all unit tests
KConfig config("ktimezonedrc");
KConfigGroup group(&config, "TimeZones");
group.writeEntry("ZoneinfoDir", m_tmp.name());
group.writeEntry("Zonetab", QString(m_tmp.name() + QString::fromLatin1("zone.tab")));
group.writeEntry("LocalZone", QString::fromLatin1("Europe/Berlin"));
config.sync();
m_project = new Project();
m_project->setId( m_project->uniqueNodeId() );
m_project->registerNodeId( m_project );
m_project->setConstraintStartTime( DateTime::fromString( "2012-02-01T00:00", KDateTime::LocalZone ) );
m_project->setConstraintEndTime( DateTime::fromString( "2013-02-01T00:00", KDateTime::LocalZone ) );
// standard worktime defines 8 hour day as default
QVERIFY( m_project->standardWorktime() );
QCOMPARE( m_project->standardWorktime()->day(), 8.0 );
m_calendar = new Calendar();
m_calendar->setName( "C1" );
m_calendar->setDefault( true );
QTime t1( 9, 0, 0 );
QTime t2 ( 17, 0, 0 );
int length = t1.msecsTo( t2 );
for ( int i=1; i <= 7; ++i ) {
CalendarDay *d = m_calendar->weekday( i );
d->setState( CalendarDay::Working );
d->addInterval( t1, length );
}
m_project->addCalendar( m_calendar );
m_task = 0;
}
void ProjectTester::cleanupTestCase()
{
delete m_project;
}
void ProjectTester::testAddTask()
{
m_task = m_project->createTask();
QVERIFY( m_project->addTask( m_task, m_project ) );
QVERIFY( m_task->parentNode() == m_project );
QCOMPARE( m_project->findNode( m_task->id() ), m_task );
m_project->takeTask( m_task );
delete m_task; m_task = 0;
}
void ProjectTester::testTakeTask()
{
m_task = m_project->createTask();
m_project->addTask( m_task, m_project );
QCOMPARE( m_project->findNode( m_task->id() ), m_task );
m_project->takeTask( m_task );
QVERIFY( m_project->findNode( m_task->id() ) == 0 );
delete ( m_task ); m_task = 0;
}
void ProjectTester::testTaskAddCmd()
{
m_task = m_project->createTask();
SubtaskAddCmd *cmd = new SubtaskAddCmd( m_project, m_task, m_project );
cmd->execute();
QVERIFY( m_task->parentNode() == m_project );
QCOMPARE( m_project->findNode( m_task->id() ), m_task );
cmd->unexecute();
QVERIFY( m_project->findNode( m_task->id() ) == 0 );
delete cmd;
m_task = 0;
}
void ProjectTester::testTaskDeleteCmd()
{
m_task = m_project->createTask();
QVERIFY( m_project->addTask( m_task, m_project ) );
QVERIFY( m_task->parentNode() == m_project );
NodeDeleteCmd *cmd = new NodeDeleteCmd( m_task );
cmd->execute();
QVERIFY( m_project->findNode( m_task->id() ) == 0 );
cmd->unexecute();
QCOMPARE( m_project->findNode( m_task->id() ), m_task );
cmd->execute();
delete cmd;
m_task = 0;
}
void ProjectTester::schedule()
{
QDate today = QDate::fromString( "2012-02-01", Qt::ISODate );
QDate tomorrow = today.addDays( 1 );
QDate yesterday = today.addDays( -1 );
QDate nextweek = today.addDays( 7 );
QTime t1( 9, 0, 0 );
QTime t2 ( 17, 0, 0 );
int length = t1.msecsTo( t2 );
Task *t = m_project->createTask();
t->setName( "T1" );
m_project->addTask( t, m_project );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Duration );
QString s = "Calculate forward, Task: Duration -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
qDebug()<<t->name()<<t->id()<<m_project->findNode( t->id() );
qDebug()<<m_project->nodeDict();
Debug::print( m_project, s, true );
ScheduleManager *sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->startTime(), m_project->startTime() );
QCOMPARE( t->endTime(), DateTime(t->startTime().addDays( 1 )) );
s = "Calculate forward, Task: Duration w calendar -------------------------------";
qDebug()<<endl<<"Testing:"<<s;
t->estimate()->setCalendar( m_calendar );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->startTime(), m_calendar->firstAvailableAfter( m_project->startTime(), m_project->endTime() ) );
QCOMPARE( t->endTime(), DateTime( t->startTime().addMSecs( length ) ) );
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
m_project->addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setAvailableFrom( QDateTime( yesterday, QTime() ) );
r->setCalendar( m_calendar );
m_project->addResource( g, r );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
t->addRequest( gr );
ResourceRequest *rr = new ResourceRequest( r, 100 );
gr->addResourceRequest( rr );
t->estimate()->setType( Estimate::Type_Effort );
s = "Calculate forward, Task: ASAP -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: ASAP, Resource 50% available -----------------";
qDebug()<<endl<<"Testing:"<<s;
r->setUnits( 50 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 1, 8, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: ASAP, Resource 50% available, Request 50% load ---------";
qDebug()<<endl<<"Testing:"<<s;
r->setUnits( 50 );
rr->setUnits( 50 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 3, 8, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: ASAP, Resource 200% available, Request 50% load ---------";
qDebug()<<endl<<"Testing:"<<s;
r->setUnits( 200 );
rr->setUnits( 50 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: ASAP, Resource 200% available, Request 100% load ---------";
qDebug()<<endl<<"Testing:"<<s;
r->setUnits( 200 );
rr->setUnits( 100 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 4, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, 2 tasks: Resource 200% available, Request 50% load each ---------";
qDebug()<<endl<<"Testing:"<<s;
r->setUnits( 200 );
rr->setUnits( 50 );
Task *task2 = m_project->createTask( *t );
task2->setName( "T2" );
m_project->addTask( task2, t );
ResourceGroupRequest *gr2 = new ResourceGroupRequest( g );
task2->addRequest( gr2 );
ResourceRequest *rr2 = new ResourceRequest( r, 50 );
gr2->addResourceRequest( rr2 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( t->plannedEffort().toHours(), 8.0 );
QCOMPARE( task2->startTime(), DateTime( today, t1 ) );
QCOMPARE( task2->endTime(), task2->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task2->plannedEffort().toHours(), 8.0 );
QVERIFY( task2->schedulingError() == false );
m_project->takeTask( task2 );
delete task2;
s = "Calculate forward, Task: ASAP, Resource available tomorrow --------";
qDebug()<<endl<<"Testing:"<<s;
r->setAvailableFrom( QDateTime( tomorrow, QTime() ) );
qDebug()<<"Tomorrow:"<<QDateTime( tomorrow, QTime() )<<r->availableFrom();
r->setUnits( 100 );
rr->setUnits( 100 );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
// Debug::print( m_project, t, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( r->availableFrom().date(), t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: ALAP -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime(0,0,0) ) );
t->setConstraint( Node::ALAP );
r->setAvailableFrom( QDateTime( yesterday, QTime() ) );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->startTime() ) );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->endTime() );
QVERIFY( t->lateFinish() >= t->endTime() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
s = "Calculate forward, Task: MustStartOn -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
r->setAvailableFrom( QDateTime( yesterday, QTime() ) );
t->setConstraint( Node::MustStartOn );
t->setConstraintStartTime( DateTime( nextweek, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->startTime(), DateTime( t->constraintStartTime().date(), t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate backwards
s = "Calculate backward, Task: MustStartOn -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( nextweek.addDays( 1 ), QTime() ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), DateTime( t->constraintStartTime().date(), t1 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate bacwords
s = "Calculate backwards, Task: MustFinishOn -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime(0,0,0) ) );
m_project->setConstraintEndTime( DateTime( nextweek.addDays( 1 ), QTime() ) );
t->setConstraint( Node::MustFinishOn );
t->setConstraintEndTime( DateTime( nextweek.addDays( -2 ), t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->endTime(), t->constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: MustFinishOn -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::MustFinishOn );
t->setConstraintEndTime( DateTime( tomorrow, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->endTime(), t->constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: StartNotEarlier -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::StartNotEarlier );
t->setConstraintStartTime( DateTime( today, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), DateTime( tomorrow, t1 ));
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate backward
s = "Calculate backwards, Task: StartNotEarlier -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( nextweek, QTime() ) );
t->setConstraint( Node::StartNotEarlier );
t->setConstraintStartTime( DateTime( today, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::print( m_project->resourceList().first(), s );
Debug::printSchedulingLog( *sm, s );
QVERIFY( t->lateStart() >= t->constraintStartTime() );
QCOMPARE( t->earlyFinish(), t->endTime() );
QVERIFY( t->lateFinish() <= m_project->constraintEndTime() );
QVERIFY( t->endTime() <= t->lateFinish() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: FinishNotLater -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::FinishNotLater );
t->setConstraintEndTime( DateTime( tomorrow.addDays( 1 ), t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->startTime() );
QVERIFY( t->startTime() >= t->earlyStart() );
QVERIFY( t->startTime() <= t->lateStart() );
QVERIFY( t->startTime() >= m_project->startTime() );
QVERIFY( t->endTime() >= t->earlyFinish() );
QVERIFY( t->endTime() <= t->lateFinish() );
QVERIFY( t->endTime() <= m_project->endTime() );
QVERIFY( t->earlyFinish() <= t->constraintEndTime() );
QVERIFY( t->lateFinish() <= m_project->constraintEndTime() );
QVERIFY( t->schedulingError() == false );
// Calculate backward
s = "Calculate backwards, Task: FinishNotLater -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( nextweek, QTime() ) );
t->setConstraint( Node::FinishNotLater );
t->setConstraintEndTime( DateTime( tomorrow, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
//Debug::print( m_project, t, s );
QCOMPARE( t->earlyStart(), m_project->startTime() );
QCOMPARE( t->lateStart(), t->startTime() );
QCOMPARE( t->earlyFinish(), t->constraintEndTime() );
QVERIFY( t->lateFinish() <= m_project->constraintEndTime() );
QCOMPARE( t->startTime(), m_project->startTime() );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forward, Task: FixedInterval -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::FixedInterval );
t->setConstraintStartTime( DateTime( tomorrow, t1 ) );
t->setConstraintEndTime( DateTime( tomorrow, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
//Debug::print( m_project, t, s );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->constraintStartTime() );
QCOMPARE( t->earlyFinish(), t->constraintEndTime() );
QCOMPARE( t->lateFinish(), t->constraintEndTime() );
QCOMPARE( t->startTime(), t->constraintStartTime() );
QCOMPARE( t->endTime(), t->constraintEndTime() );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: FixedInterval -----------------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::FixedInterval );
t->setConstraintStartTime( DateTime( tomorrow, QTime() ) ); // outside working hours
t->setConstraintEndTime( DateTime( tomorrow, t2 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->constraintStartTime() );
QCOMPARE( t->earlyFinish(), t->constraintEndTime() );
QCOMPARE( t->lateFinish(), t->constraintEndTime() );
QCOMPARE( t->startTime(), t->constraintStartTime() );
QCOMPARE( t->endTime(), t->constraintEndTime() );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: Milestone, ASAP-------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::ASAP );
t->estimate()->clear();
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
//Debug::print( m_project, t, s );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->earlyStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->earlyStart() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate backward
s = "Calculate backwards, Task: Milestone, ASAP-------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( today, QTime() ) );
t->setConstraint( Node::ASAP );
t->estimate()->clear();
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->earlyStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->earlyStart() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: Milestone, ALAP-------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::ALAP );
t->estimate()->clear();
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->earlyStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->earlyStart() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate backward
s = "Calculate backwards, Task: Milestone, ALAP-------------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( today, QTime() ) );
t->setConstraint( Node::ALAP );
t->estimate()->clear();
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->earlyStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->earlyStart() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: Milestone, MustStartOn ------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::MustStartOn );
t->setConstraintStartTime( DateTime( tomorrow, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->constraintStartTime() );
QCOMPARE( t->earlyFinish(), t->lateStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate backward
s = "Calculate backwards, Task: Milestone, MustStartOn ------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( tomorrow, QTime() ) );
t->setConstraint( Node::MustStartOn );
t->setConstraintStartTime( DateTime( today, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), t->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->earlyStart() );
QCOMPARE( t->lateFinish(), m_project->constraintEndTime() );
QCOMPARE( t->startTime(), t->constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
// Calculate forward
s = "Calculate forwards, Task: Milestone, MustFinishOn ------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::MustFinishOn );
t->setConstraintEndTime( DateTime( tomorrow, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->constraintEndTime() );
QCOMPARE( t->earlyFinish(), t->lateStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->constraintEndTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->endTime(), t->endTime() );
// Calculate backward
s = "Calculate backwards, Task: Milestone, MustFinishOn ------------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( tomorrow, QTime() ) );
t->setConstraint( Node::MustFinishOn );
t->setConstraintEndTime( DateTime( today, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), t->constraintEndTime() );
QCOMPARE( t->lateStart(), t->constraintEndTime() );
QCOMPARE( t->earlyFinish(), t->lateStart() );
QCOMPARE( t->lateFinish(), m_project->constraintEndTime() );
QCOMPARE( t->startTime(), t->constraintEndTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->startTime(), t->startTime() );
// Calculate forward
s = "Calculate forwards, Task: Milestone, StartNotEarlier ---------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::StartNotEarlier );
t->setConstraintEndTime( DateTime( tomorrow, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), m_project->constraintStartTime() );
QCOMPARE( t->lateStart(), t->constraintStartTime() );
QCOMPARE( t->earlyFinish(), t->lateStart() );
QCOMPARE( t->lateFinish(), t->earlyFinish() );
QCOMPARE( t->startTime(), t->constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->endTime(), t->endTime() );
// Calculate backward
s = "Calculate backwards, Task: Milestone, StartNotEarlier ---------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( tomorrow, QTime() ) );
t->setConstraint( Node::StartNotEarlier );
t->setConstraintStartTime( DateTime( today, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, s, true );
QVERIFY( t->earlyStart() >= t->constraintStartTime() );
QVERIFY( t->lateStart() >= t->earlyStart() );
QVERIFY( t->earlyFinish() <= t->lateFinish() );
QVERIFY( t->lateFinish() >= t->constraintStartTime() );
QVERIFY( t->startTime() >= t->constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->startTime(), t->startTime() );
// Calculate forward
s = "Calculate forwards, Task: Milestone, FinishNotLater ---------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::FinishNotLater );
t->setConstraintEndTime( DateTime( tomorrow, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QVERIFY( t->earlyStart() <= t->constraintEndTime() );
QVERIFY( t->lateStart() <= t->constraintEndTime() );
QVERIFY( t->earlyFinish() >= t->earlyStart() );
QVERIFY( t->lateFinish() >= t->earlyFinish() );
QVERIFY( t->startTime() <= t->constraintEndTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->endTime(), t->endTime() );
// Calculate backward
s = "Calculate backwards, Task: Milestone, FinishNotLater ---------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintEndTime( DateTime( tomorrow, QTime() ) );
t->setConstraint( Node::FinishNotLater );
t->setConstraintEndTime( DateTime( today, t1 ) );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setSchedulingDirection( true );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
QCOMPARE( t->earlyStart(), t->constraintStartTime() );
QCOMPARE( t->lateStart(), t->earlyStart() );
QCOMPARE( t->earlyFinish(), t->lateStart() );
QCOMPARE( t->lateFinish(), m_project->constraintEndTime() );
QCOMPARE( t->startTime(), t->constraintEndTime() );
QCOMPARE( t->endTime(), t->startTime() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( m_project->startTime(), t->startTime() );
// Calculate forward
s = "Calculate forward, 2 Tasks, no overbooking ----------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
m_project->setConstraintEndTime( DateTime( today, QTime() ).addDays( 4 ) );
t->setConstraint( Node::ASAP );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 2.0 );
Task *tsk2 = m_project->createTask( *t );
tsk2->setName( "T2" );
m_project->addTask( tsk2, m_project );
gr = new ResourceGroupRequest( g );
tsk2->addRequest( gr );
rr = new ResourceRequest( r, 100 );
gr->addResourceRequest( rr );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setAllowOverbooking( false );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
Debug::print( m_project, t, s );
Debug::print( m_project, tsk2, s );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->constraintStartTime() ) );
QCOMPARE( t->lateStart(), tsk2->startTime() );
QCOMPARE( t->earlyFinish(), DateTime( tomorrow, t2 ) );
QCOMPARE( t->lateFinish(), t->lateFinish() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->earlyFinish() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( tsk2->earlyStart(), t->earlyStart() );
QCOMPARE( tsk2->lateStart(), t->earlyFinish() + Duration( 0, 16, 0 ) );
QCOMPARE( tsk2->earlyFinish(), DateTime( tomorrow, t2 ) );
QCOMPARE( tsk2->lateFinish(), t->lateFinish() );
QCOMPARE( tsk2->startTime(), DateTime( tomorrow.addDays( 1 ), t1 ) );
QCOMPARE( tsk2->endTime(), tsk2->lateFinish() );
QVERIFY( tsk2->schedulingError() == false );
QCOMPARE( m_project->endTime(), tsk2->endTime() );
// Calculate forward
s = "Calculate forward, 2 Tasks, relation ---------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( DateTime( today, QTime() ) );
t->setConstraint( Node::ASAP );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 2.0 );
Relation *rel = new Relation( t, tsk2 );
bool relationAdded = m_project->addRelation( rel );
QVERIFY( relationAdded );
sm = m_project->createScheduleManager( "Test Plan" );
sm->setAllowOverbooking( true );
sm->setSchedulingDirection( false );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
// Debug::print( m_project, t, s );
// Debug::print( m_project, tsk2, s );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->earlyStart(), t->requests().workTimeAfter( m_project->constraintStartTime() ) );
QCOMPARE( t->lateStart(), DateTime( today, t1 ) );
QCOMPARE( t->earlyFinish(), DateTime( tomorrow, t2 ) );
QCOMPARE( t->lateFinish(), t->lateFinish() );
QCOMPARE( t->startTime(), DateTime( today, t1 ) );
QCOMPARE( t->endTime(), t->earlyFinish() );
QVERIFY( t->schedulingError() == false );
QCOMPARE( tsk2->earlyStart(), tsk2->requests().workTimeAfter( t->earlyFinish() ) );
QCOMPARE( tsk2->lateStart(), DateTime( tomorrow.addDays( 1 ), t1 ) );
QCOMPARE( tsk2->earlyFinish(), DateTime( tomorrow.addDays( 2 ), t2 ) );
QCOMPARE( tsk2->lateFinish(), tsk2->earlyFinish() );
QCOMPARE( tsk2->startTime(), DateTime( tomorrow.addDays( 1 ), t1 ) );
QCOMPARE( tsk2->endTime(), tsk2->earlyFinish() );
QVERIFY( tsk2->schedulingError() == false );
QCOMPARE( m_project->endTime(), tsk2->endTime() );
}
void ProjectTester::scheduleFullday()
{
QString s = "Full day, 1 resource works 24 hours a day -------------";
qDebug()<<endl<<"Testing:"<<s;
m_project->setConstraintStartTime( QDateTime::fromString( "2011-09-01T00:00:00", Qt::ISODate) );
m_project->setConstraintEndTime( QDateTime::fromString( "2011-09-16T00:00:00", Qt::ISODate) );
qDebug()<<m_project->constraintStartTime()<<m_project->constraintEndTime();
Calendar *c = new Calendar("Test");
QTime t1(0,0,0);
int length = 24*60*60*1000;
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
m_project->addCalendar( c );
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
m_project->addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setCalendar( c );
r->setAvailableFrom( m_project->constraintStartTime() );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
m_project->addResource( g, r );
Task *t = m_project->createTask();
t->setName( "T1" );
t->setId( m_project->uniqueNodeId() );
m_project->addTask( t, m_project );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 3 * 14.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
t->addRequest( gr );
ScheduleManager *sm = m_project->createScheduleManager( "Test Plan" );
m_project->addScheduleManager( sm );
sm->createSchedules();
m_project->calculate( *sm );
// Debug::print( c, s );
// Debug::print( m_project, t, s );
// Debug::print( r, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), m_project->startTime() );
QCOMPARE( t->endTime(), DateTime( t->startTime().addDays( 14 ) ) );
s = "Full day, 8 hour shifts, 3 resources ---------------";
qDebug()<<endl<<"Testing:"<<s;
int hour = 60*60*1000;
Calendar *c1 = new Calendar("Test 1");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c1->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 6, 0, 0 ), 8*hour));
}
m_project->addCalendar( c1 );
Calendar *c2 = new Calendar("Test 2");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c2->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 14, 0, 0 ), 8*hour));
}
m_project->addCalendar( c2 );
Calendar *c3 = new Calendar("Test 3");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c3->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 0, 0, 0 ), 6*hour));
wd1->addInterval(TimeInterval(QTime( 22, 0, 0 ), 2*hour));
}
m_project->addCalendar( c3 );
r->setCalendar( c1 );
r = new Resource();
r->setName( "R2" );
r->setCalendar( c2 );
r->setAvailableFrom( m_project->constraintStartTime() );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
m_project->addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
r = new Resource();
r->setName( "R3" );
r->setCalendar( c3 );
r->setAvailableFrom( m_project->constraintStartTime() );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
m_project->addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
sm->createSchedules();
m_project->calculate( *sm );
// Debug::print( m_project, t, s );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), m_project->startTime() );
QCOMPARE( t->endTime(), DateTime(t->startTime().addDays( 14 )) );
}
void ProjectTester::scheduleFulldayDstSpring()
{
QString s = "Daylight saving time - Spring, 1 resource works 24 hours a day -------------";
qDebug()<<endl<<"Testing:"<<s;
Project project;
project.setName( "DST" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
project.setConstraintStartTime( DateTime( QDate::fromString( "2011-03-25", Qt::ISODate) ) );
project.setConstraintEndTime( DateTime( QDate::fromString( "2011-03-29", Qt::ISODate) ) );
qDebug()<<project.constraintStartTime()<<project.constraintEndTime();
Calendar *c = new Calendar("Test");
QTime t1(0,0,0);
int length = 24*60*60*1000;
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setCalendar( c );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
Task *t = project.createTask();
t->setName( "T1" );
t->setId( project.uniqueNodeId() );
project.addTask( t, &project );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 3 * 4.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
t->addRequest( gr );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( c, s );
Debug::print( &project, t, s );
// Debug::print( r, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), project.constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() + Duration( 4, 0, 0 ) );
s = "Daylight saving time - Spring, Backward: 1 resource works 24 hours a day -------------";
qDebug()<<endl<<"Testing:"<<s;
// make room for the task
project.setConstraintStartTime( DateTime( QDate::fromString( "2011-03-24", Qt::ISODate) ) );
sm = project.createScheduleManager( "Test Backward" );
project.addScheduleManager( sm );
sm->setSchedulingDirection( true );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( c, s );
Debug::print( &project, t, s );
// Debug::print( r, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->endTime(), project.constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 4, 0, 0 ) );
s = "Daylight saving time - Spring, 8 hour shifts, 3 resources ---------------";
qDebug()<<endl<<"Testing:"<<s;
int hour = 60*60*1000;
Calendar *c1 = new Calendar("Test 1");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c1->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 6, 0, 0 ), 8*hour));
}
project.addCalendar( c1 );
Calendar *c2 = new Calendar("Test 2");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c2->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 14, 0, 0 ), 8*hour));
}
project.addCalendar( c2 );
Calendar *c3 = new Calendar("Test 3");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c3->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 0, 0, 0 ), 6*hour));
wd1->addInterval(TimeInterval(QTime( 22, 0, 0 ), 2*hour));
}
project.addCalendar( c3 );
r->setCalendar( c1 );
r = new Resource();
r->setName( "R2" );
r->setCalendar( c2 );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
r = new Resource();
r->setName( "R3" );
r->setCalendar( c3 );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
project.setConstraintStartTime( DateTime( QDate::fromString( "2011-03-25", Qt::ISODate) ) );
sm = project.createScheduleManager( "Test Foreward" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( &project, t, s );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime().toUTC(), project.constraintStartTime().toUTC() );
QCOMPARE( t->endTime(), t->startTime() + Duration( 4, 0, 0 ) );
s = "Daylight saving time - Spring, Backward: 8 hour shifts, 3 resources ---------------";
qDebug()<<endl<<"Testing:"<<s;
project.setConstraintStartTime( DateTime( QDate::fromString( "2011-03-24", Qt::ISODate) ) );
sm = project.createScheduleManager( "Test Backward" );
project.addScheduleManager( sm );
sm->setSchedulingDirection( true );
sm->createSchedules();
project.calculate( *sm );
Debug::print( &project, t, s );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->endTime(), project.constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 4, 0, 0 ) );
}
void ProjectTester::scheduleFulldayDstFall()
{
QString s = "Daylight saving time - Fall, 1 resource works 24 hours a day -------------";
qDebug()<<endl<<"Testing:"<<s;
Project project;
project.setName( "DST" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
project.setConstraintStartTime( QDateTime::fromString( "2011-10-28T00:00:00", Qt::ISODate) );
project.setConstraintEndTime( QDateTime::fromString( "2011-11-01T00:00:00", Qt::ISODate) );
qDebug()<<project.constraintStartTime()<<project.constraintEndTime();
Calendar *c = new Calendar("Test");
QTime t1(0,0,0);
int length = 24*60*60*1000;
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setCalendar( c );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
Task *t = project.createTask();
t->setName( "T1" );
t->setId( project.uniqueNodeId() );
project.addTask( t, &project );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 3 * 4.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
t->addRequest( gr );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( c, s );
Debug::print( &project, t, s );
// Debug::print( r, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), project.constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() + Duration( 4, 0, 0 ) );
s = "Daylight saving time - Fall, Backward: 1 resource works 24 hours a day -------------";
qDebug()<<endl<<"Testing:"<<s;
sm = project.createScheduleManager( "Test Backward" );
project.addScheduleManager( sm );
sm->setSchedulingDirection( true );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( c, s );
Debug::print( &project, t, s );
// Debug::print( r, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->endTime(), project.constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 4, 0, 0 ) );
s = "Daylight saving time - Fall, 8 hour shifts, 3 resources ---------------";
qDebug()<<endl<<"Testing:"<<s;
int hour = 60*60*1000;
Calendar *c1 = new Calendar("Test 1");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c1->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 6, 0, 0 ), 8*hour));
}
project.addCalendar( c1 );
Calendar *c2 = new Calendar("Test 2");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c2->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 14, 0, 0 ), 8*hour));
}
project.addCalendar( c2 );
Calendar *c3 = new Calendar("Test 3");
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c3->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(QTime( 0, 0, 0 ), 6*hour));
wd1->addInterval(TimeInterval(QTime( 22, 0, 0 ), 2*hour));
}
project.addCalendar( c3 );
r->setCalendar( c1 );
r = new Resource();
r->setName( "R2" );
r->setCalendar( c2 );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
r = new Resource();
r->setName( "R3" );
r->setCalendar( c3 );
r->setAvailableFrom( project.constraintStartTime().addDays( -1 ) );
r->setAvailableUntil( r->availableFrom().addDays( 21 ) );
project.addResource( g, r );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
sm = project.createScheduleManager( "Test Foreward" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( &project, t, s );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), project.constraintStartTime() );
QCOMPARE( t->endTime(), t->startTime() + Duration( 4, 0, 0 ) );
s = "Daylight saving time - Fall, Backward: 8 hour shifts, 3 resources ---------------";
qDebug()<<endl<<"Testing:"<<s;
sm = project.createScheduleManager( "Test Foreward" );
project.addScheduleManager( sm );
sm->setSchedulingDirection( true );
sm->createSchedules();
project.calculate( *sm );
QCOMPARE( t->endTime(), project.constraintEndTime() );
QCOMPARE( t->startTime(), t->endTime() - Duration( 4, 0, 0 ) );
}
void ProjectTester::scheduleWithExternalAppointments()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate::fromString( "2012-02-01", Qt::ISODate ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 3 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar c("Test");
QTime t1(0,0,0);
int length = 24*60*60*1000;
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c.weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setCalendar( &c );
project.addResource( g, r );
r->addExternalAppointment( "Ext-1", "External project 1", targetstart, targetstart.addDays( 1 ), 100 );
r->addExternalAppointment( "Ext-1", "External project 1", targetend.addDays( -1 ), targetend, 100 );
Task *t = project.createTask();
t->setName( "T1" );
project.addTask( t, &project );
t->estimate()->setUnit( Duration::Unit_h );
t->estimate()->setExpectedEstimate( 8.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
t->addRequest( gr );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
QString s = "Schedule with external appointments ----------";
qDebug()<<endl<<"Testing:"<<s;
Debug::print( r, s );
Debug::print( &project, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), targetstart + Duration( 1, 0, 0 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
sm->setAllowOverbooking( true );
sm->createSchedules();
project.calculate( *sm );
// Debug::printSchedulingLog( *sm );
QCOMPARE( t->startTime(), targetstart );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
sm->setAllowOverbooking( false );
sm->setSchedulingDirection( true ); // backwards
sm->createSchedules();
project.calculate( *sm );
Debug::print( &project, s, true );
Debug::print( r, "", true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( t->startTime(), targetend - Duration( 1, 8, 0 ) );
QCOMPARE( t->endTime(), t->startTime() + Duration( 0, 8, 0 ) );
sm->setAllowOverbooking( true );
sm->createSchedules();
project.calculate( *sm );
// Debug::printSchedulingLog( *sm );
QCOMPARE( t->startTime(), targetend - Duration( 0, 8, 0 ) );
QCOMPARE( t->endTime(), targetend );
sm->setAllowOverbooking( false );
r->clearExternalAppointments();
sm->createSchedules();
project.calculate( *sm );
// Debug::printSchedulingLog( *sm );
QCOMPARE( t->endTime(), targetend );
QCOMPARE( t->startTime(), t->endTime() - Duration( 0, 8, 0 ) );
}
void ProjectTester::reschedule()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate::fromString( "2012-02-01", Qt::ISODate ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
ResourceGroup *g = new ResourceGroup();
g->setName( "G1" );
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "R1" );
r->setCalendar( c );
project.addResource( g, r );
QString s = "Re-schedule; schedule tasks T1, T2, T3 ---------------";
qDebug()<<endl<<"Testing:"<<s;
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_h );
task1->estimate()->setExpectedEstimate( 8.0 );
task1->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
task1->addRequest( gr );
Task *task2 = project.createTask();
task2->setName( "T2" );
project.addTask( task2, &project );
task2->estimate()->setUnit( Duration::Unit_h );
task2->estimate()->setExpectedEstimate( 8.0 );
task2->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
task2->addRequest( gr );
Task *task3 = project.createTask();
task3->setName( "T3" );
project.addTask( task3, &project );
task3->estimate()->setUnit( Duration::Unit_h );
task3->estimate()->setExpectedEstimate( 8.0 );
task3->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
gr->addResourceRequest( new ResourceRequest( r, 100 ) );
task3->addRequest( gr );
Relation *rel = new Relation( task1, task2 );
project.addRelation( rel );
rel = new Relation( task1, task3 );
project.addRelation( rel );
ScheduleManager *sm = project.createScheduleManager( "Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( &project, task1, s, true );
// Debug::print( &project, task2, s, true );
// Debug::print( &project, task3, s, true );
// Debug::print( r, s );
// Debug::printSchedulingLog( *sm );
QVERIFY( task1->startTime() >= c->firstAvailableAfter( targetstart, targetend ) );
QVERIFY( task1->startTime() <= c->firstAvailableBefore( targetend, targetstart ) );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( task2->startTime() >= c->firstAvailableAfter( targetstart, targetend ) );
QVERIFY( task2->startTime() <= c->firstAvailableBefore( targetend, targetstart ) );
QCOMPARE( task2->endTime(), task2->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( task3->startTime() >= c->firstAvailableAfter( targetstart, targetend ) );
QVERIFY( task3->startTime() <= c->firstAvailableBefore( targetend, targetstart ) );
QCOMPARE( task3->endTime(), task3->startTime() + Duration( 0, 8, 0 ) );
DateTime restart = task1->endTime();
s = QString( "Re-schedule; re-schedule from %1 - tasks T1 (finished), T2, T3 ------" ).arg( restart.toString() );
qDebug()<<endl<<"Testing:"<<s;
task1->completion().setStarted( true );
task1->completion().setPercentFinished( task1->endTime().date(), 100 );
task1->completion().setFinished( true );
ScheduleManager *child = project.createScheduleManager( "Plan.1" );
project.addScheduleManager( child, sm );
child->setRecalculate( true );
child->setRecalculateFrom( restart );
child->createSchedules();
project.calculate( *child );
// Debug::print( &project, task1, s, true );
// Debug::print( &project, task2, s, true );
// Debug::print( &project, task3, s, true );
// Debug::printSchedulingLog( *child, s );
QCOMPARE( task1->startTime(), c->firstAvailableAfter( targetstart, targetend ) );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 8, 0 ) );
// either task2 or task3 may be scheduled first
if ( task2->startTime() < task3->startTime() ) {
QCOMPARE( task2->startTime(), c->firstAvailableAfter( qMax(task1->endTime(), restart ), targetend ) );
QCOMPARE( task2->endTime(), task2->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task3->startTime(), c->firstAvailableAfter( task2->endTime(), targetend ) );
QCOMPARE( task3->endTime(), task3->startTime() + Duration( 0, 8, 0 ) );
} else {
QCOMPARE( task3->startTime(), c->firstAvailableAfter( qMax(task1->endTime(), restart ), targetend ) );
QCOMPARE( task3->endTime(), task3->startTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task2->startTime(), c->firstAvailableAfter( task3->endTime(), targetend ) );
QCOMPARE( task2->endTime(), task2->startTime() + Duration( 0, 8, 0 ) );
}
}
void ProjectTester::materialResource()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate::fromString( "2012-02-01", Qt::ISODate ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_h );
task1->estimate()->setExpectedEstimate( 8.0 );
task1->estimate()->setType( Estimate::Type_Effort );
QString s = "Calculate forward, Task: ASAP, Working + material resource --------";
qDebug()<<endl<<"Testing:"<<s;
qDebug()<<s;
ResourceGroup *g = new ResourceGroup();
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "Work" );
r->setAvailableFrom( targetstart );
r->setCalendar( c );
project.addResource( g, r );
ResourceGroup *mg = new ResourceGroup();
mg->setType( ResourceGroup::Type_Material );
project.addResourceGroup( mg );
Resource *mr = new Resource();
mr->setType( Resource::Type_Material );
mr->setName( "Material" );
mr->setAvailableFrom( targetstart );
mr->setCalendar( c );
project.addResource( mg, mr );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
ResourceRequest *rr = new ResourceRequest( r, 100 );
gr->addResourceRequest( rr );
ResourceGroupRequest *mgr = new ResourceGroupRequest( mg );
task1->addRequest( mgr );
ResourceRequest *mrr = new ResourceRequest( mr, 100 );
mgr->addResourceRequest( mrr );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r, s);
// Debug::print( mr, s);
// Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->earlyStart(), task1->requests().workTimeAfter( targetstart ) );
QVERIFY( task1->lateStart() >= task1->earlyStart() );
QVERIFY( task1->earlyFinish() <= task1->endTime() );
QVERIFY( task1->lateFinish() >= task1->endTime() );
QCOMPARE( task1->startTime(), DateTime( r->availableFrom().date(), t1 ) );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( task1->schedulingError() == false );
}
void ProjectTester::requiredResource()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate::fromString( "2012-02-01", Qt::ISODate ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_h );
task1->estimate()->setExpectedEstimate( 8.0 );
task1->estimate()->setType( Estimate::Type_Effort );
QString s = "Required resource: Working + required material resource --------";
qDebug()<<endl<<"Testing:"<<s;
ResourceGroup *g = new ResourceGroup();
project.addResourceGroup( g );
Resource *r = new Resource();
r->setName( "Work" );
r->setAvailableFrom( targetstart );
r->setCalendar( c );
project.addResource( g, r );
ResourceGroup *mg = new ResourceGroup();
mg->setType( ResourceGroup::Type_Material );
mg->setName( "MG" );
project.addResourceGroup( mg );
Resource *mr = new Resource();
mr->setType( Resource::Type_Material );
mr->setName( "Material" );
mr->setAvailableFrom( targetstart );
mr->setCalendar( c );
project.addResource( mg, mr );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
ResourceRequest *rr = new ResourceRequest( r, 100 );
gr->addResourceRequest( rr );
QList<Resource*> lst; lst << mr;
rr->setRequiredResources( lst );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r, s);
// Debug::print( mr, s);
// Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->earlyStart(), task1->requests().workTimeAfter( targetstart ) );
QVERIFY( task1->lateStart() >= task1->earlyStart() );
QVERIFY( task1->earlyFinish() <= task1->endTime() );
QVERIFY( task1->lateFinish() >= task1->endTime() );
QCOMPARE( task1->startTime(), DateTime( r->availableFrom().date(), t1 ) );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( task1->schedulingError() == false );
QList<Appointment*> apps = r->appointments( sm->scheduleId() );
QVERIFY( apps.count() == 1 );
QCOMPARE( task1->startTime(), apps.first()->startTime() );
QCOMPARE( task1->endTime(), apps.last()->endTime() );
apps = mr->appointments( sm->scheduleId() );
QVERIFY( apps.count() == 1 );
QCOMPARE( task1->startTime(), apps.first()->startTime() );
QCOMPARE( task1->endTime(), apps.last()->endTime() );
s = "Required resource limits availability --------";
qDebug()<<endl<<"Testing:"<<s;
DateTime tomorrow = targetstart.addDays( 1 );
mr->setAvailableFrom( tomorrow );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r, s);
// Debug::print( mr, s);
// Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->earlyStart(), task1->requests().workTimeAfter( targetstart ) );
QVERIFY( task1->lateStart() >= task1->earlyStart() );
QVERIFY( task1->earlyFinish() <= task1->endTime() );
QVERIFY( task1->lateFinish() >= task1->endTime() );
QCOMPARE( task1->startTime(), DateTime( mr->availableFrom().date(), t1 ) );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 8, 0 ) );
QVERIFY( task1->schedulingError() == false );
apps = r->appointments( sm->scheduleId() );
QVERIFY( apps.count() == 1 );
QCOMPARE( task1->startTime(), apps.first()->startTime() );
QCOMPARE( task1->endTime(), apps.last()->endTime() );
apps = mr->appointments( sm->scheduleId() );
QVERIFY( apps.count() == 1 );
QCOMPARE( task1->startTime(), apps.first()->startTime() );
QCOMPARE( task1->endTime(), apps.last()->endTime() );
}
void ProjectTester::resourceWithLimitedAvailability()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate( 2010, 5, 1 ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
DateTime expectedEndTime( QDate( 2010, 5, 3 ), QTime( 16, 0, 0 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_d );
task1->estimate()->setExpectedEstimate( 4.0 );
task1->estimate()->setType( Estimate::Type_Effort );
QString s = "Two resources: One with available until < resulting task length --------";
qDebug()<<endl<<"Testing:"<<s;
ResourceGroup *g = new ResourceGroup();
project.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
r1->setAvailableFrom( targetstart );
r1->setCalendar( c );
project.addResource( g, r1 );
Resource *r2 = new Resource();
r2->setName( "R2" );
r2->setAvailableFrom( targetstart );
r2->setAvailableUntil( targetstart.addDays( 1 ) );
r2->setCalendar( c );
project.addResource( g, r2 );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
ResourceRequest *rr1 = new ResourceRequest( r1, 100 );
gr->addResourceRequest( rr1 );
ResourceRequest *rr2 = new ResourceRequest( r2, 100 );
gr->addResourceRequest( rr2 );
ScheduleManager *sm = project.createScheduleManager( "Test Plan" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
// Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), expectedEndTime );
}
void ProjectTester::unavailableResource()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate( 2010, 5, 1 ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_d );
task1->estimate()->setExpectedEstimate( 2.0 );
task1->estimate()->setType( Estimate::Type_Effort );
QString s = "One available resource --------";
qDebug()<<endl<<"Testing:"<<s;
ResourceGroup *g = new ResourceGroup();
project.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
r1->setCalendar( c );
project.addResource( g, r1 );
Resource *r2 = new Resource();
r2->setName( "Unavailable" );
r2->setCalendar( c );
project.addResource( g, r2 );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
gr->addResourceRequest( new ResourceRequest( r1, 100 ) );
ScheduleManager *sm = project.createScheduleManager( "Plan R1" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
DateTime expectedEndTime = targetstart + Duration( 1, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
s = "One available resource + one unavailable resource --------";
qDebug()<<endl<<"Testing:"<<s;
r2->setAvailableFrom( targetend );
r2->setAvailableUntil( targetend.addDays( 1 ) );
gr->addResourceRequest( new ResourceRequest( r2, 100 ) );
sm = project.createScheduleManager( "Team + Resource" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), expectedEndTime );
}
void ProjectTester::team()
{
Project project;
project.setName( "P1" );
project.setId( project.uniqueNodeId() );
project.registerNodeId( &project );
DateTime targetstart = DateTime( QDate( 2010, 5, 1 ), QTime(0,0,0) );
DateTime targetend = DateTime( targetstart.addDays( 7 ) );
project.setConstraintStartTime( targetstart );
project.setConstraintEndTime( targetend);
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
project.addCalendar( c );
Task *task1 = project.createTask();
task1->setName( "T1" );
project.addTask( task1, &project );
task1->estimate()->setUnit( Duration::Unit_d );
task1->estimate()->setExpectedEstimate( 2.0 );
task1->estimate()->setType( Estimate::Type_Effort );
QString s = "One team with one resource --------";
qDebug()<<endl<<"Testing:"<<s;
ResourceGroup *g = new ResourceGroup();
project.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
r1->setCalendar( c );
project.addResource( g, r1 );
Resource *r2 = new Resource();
r2->setName( "Team member" );
r2->setCalendar( c );
project.addResource( g, r2 );
Resource *team = new Resource();
team->setType( Resource::Type_Team );
team->setName( "Team" );
project.addResource( g, team );
team->addTeamMemberId( r2->id() );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
ResourceRequest *tr = new ResourceRequest( team, 100 );
gr->addResourceRequest( tr );
ScheduleManager *sm = project.createScheduleManager( "Team" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( team, s, false);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
DateTime expectedEndTime = targetstart + Duration( 1, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
s = "One team with one resource + one resource --------";
qDebug()<<endl<<"Testing:"<<s;
ResourceRequest *rr1 = new ResourceRequest( r1, 100 );
gr->addResourceRequest( rr1 );
sm = project.createScheduleManager( "Team + Resource" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( team, s, false);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
expectedEndTime = targetstart + Duration( 0, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
s = "One team with one resource + one resource, resource available too late --------";
qDebug()<<endl<<"Testing:"<<s;
r1->setAvailableFrom( targetend );
r1->setAvailableUntil( targetend.addDays( 7 ) );
sm = project.createScheduleManager( "Team + Resource not available" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( team, s, false);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
expectedEndTime = targetstart + Duration( 1, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
s = "One team with two resources --------";
qDebug()<<endl<<"Testing:"<<s;
r1->removeRequests();
team->addTeamMemberId( r1->id() );
r1->setAvailableFrom( targetstart );
r1->setAvailableUntil( targetend );
sm = project.createScheduleManager( "Team with 2 resources" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
// Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( team, s, false);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
expectedEndTime = targetstart + Duration( 0, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
s = "One team with two resources, one resource unavailable --------";
qDebug()<<endl<<"Testing:"<<s;
r1->setAvailableFrom( targetend );
r1->setAvailableUntil( targetend.addDays( 2 ) );
sm = project.createScheduleManager( "Team, one unavailable resource" );
project.addScheduleManager( sm );
sm->createSchedules();
project.calculate( *sm );
Debug::print( r1, s);
// Debug::print( r2, s);
Debug::print( team, s, false);
Debug::print( &project, task1, s);
// Debug::printSchedulingLog( *sm, s );
expectedEndTime = targetstart + Duration( 1, 16, 0 );
QCOMPARE( task1->endTime(), expectedEndTime );
gr->takeResourceRequest(tr);
task1->takeRequest(gr);
project.takeResource( g, team);
team->removeTeamMemberId( r2->id() );
}
void ProjectTester::inWBSOrder()
{
Project p;
p.setName( "WBS Order" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2012-02-01", Qt::ISODate );
st = DateTime( st.addDays( 1 ) );
st.setTime( QTime ( 0, 0, 0 ) );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *t = p.createTask();
t->setName( "T1" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
t->addRequest( gr );
ResourceRequest *tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T2" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T3" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T4" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
ScheduleManager *sm = p.createScheduleManager( "WBS Order, forward" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
QString s = "Schedule 4 tasks forward in wbs order -------";
// NOTE: It's not *mandatory* to schedule in wbs order but users expect it, so we'll try
// This test can be removed if for some important reason this isn't possible.
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 3, 8, 0 ) );
}
void ProjectTester::resourceConflictALAP()
{
Project p;
p.setName( "resourceConflictALAP" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2012-02-01", Qt::ISODate );
st = DateTime( st.addDays( 1 ) );
st.setTime( QTime ( 0, 0, 0 ) );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *t = p.createTask();
t->setName( "T1" );
t->setConstraint( Node::ALAP );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
t->addRequest( gr );
ResourceRequest *tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T2" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T3" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T4" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
ScheduleManager *sm = p.createScheduleManager( "T1 ALAP" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
QString s = "Schedule T1 ALAP -------";
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule T1, T2 ALAP -------";
p.allTasks().at( 1 )->setConstraint( Node::ALAP );
sm = p.createScheduleManager( "T1, T2 ALAP" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule T1, T2, T3 ALAP -------";
p.allTasks().at( 2 )->setConstraint( Node::ALAP );
sm = p.createScheduleManager( "T1, T2, T3 ALAP" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule T1, T2, T3, T4 ALAP -------";
p.allTasks().at( 3 )->setConstraint( Node::ALAP );
sm = p.createScheduleManager( "T1, T2, T3, T4 ALAP" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
}
void ProjectTester::resourceConflictMustStartOn()
{
Project p;
p.setName( "resourceConflictMustStartOn" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2012-02-01T00:00:00", Qt::ISODate );
st = DateTime( st.addDays( 1 ) );
st.setTime( QTime ( 0, 0, 0 ) );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *t = p.createTask();
t->setName( "T1" );
t->setConstraint( Node::MustStartOn );
t->setConstraintStartTime( st + Duration( 1, 8, 0 ) );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
t->addRequest( gr );
ResourceRequest *tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T2" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T3" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
t = p.createTask();
t->setName( "T4" );
p.addSubTask( t, &p );
t->estimate()->setUnit( Duration::Unit_d );
t->estimate()->setExpectedEstimate( 1.0 );
t->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
t->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
ScheduleManager *sm = p.createScheduleManager( "T1 MustStartOn" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
QString s = "Schedule T1 MustStartOn -------";
// Debug::print ( c, s );
// Debug::print( r1, s );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule T1, T2 MustStartOn -------";
p.allTasks().at( 1 )->setConstraint( Node::MustStartOn );
p.allTasks().at( 1 )->setConstraintStartTime( st + Duration( 2, 8, 0 ) );
sm = p.createScheduleManager( "T1, T2 MustStartOn" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
// Debug::print ( c, s );
// Debug::print( r1, s );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule T1, T2, T3 MustStartOn -------";
p.allTasks().at( 2 )->setConstraint( Node::MustStartOn );
p.allTasks().at( 2 )->setConstraintStartTime( st + Duration( 3, 8, 0 ) );
sm = p.createScheduleManager( "T1, T2, T3 MustStartOn" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
// Debug::print ( c, s );
// Debug::print( r1, s );
// Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( p.allTasks().count(), 4 );
QCOMPARE( p.allTasks().at( 0 )->startTime(), st + Duration( 1, 8, 0 ) );
QCOMPARE( p.allTasks().at( 0 )->endTime(), st + Duration( 1, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->startTime(), st + Duration( 2, 8, 0 ) );
QCOMPARE( p.allTasks().at( 1 )->endTime(), st + Duration( 2, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->startTime(), st + Duration( 3, 8, 0 ) );
QCOMPARE( p.allTasks().at( 2 )->endTime(), st + Duration( 3, 8, 0 ) + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->startTime(), st + Duration( 0, 8, 0 ) );
QCOMPARE( p.allTasks().at( 3 )->endTime(), st + Duration( 0, 8, 0 ) + Duration( 0, 8, 0 ) );
s = "Schedule backwards, T1 MustStartOn, T2 ASAP -------";
p.takeTask( p.allTasks().at( 3 ), false );
p.takeTask( p.allTasks().at( 2 ), false );
Task *task1 = p.allTasks().at( 0 );
Task *task2 = p.allTasks().at( 1 );
DateTime et = p.constraintEndTime();
task1->setConstraint( Node::MustStartOn );
task1->setConstraintStartTime( et - Duration( 1, 16, 0 ) );
task2->setConstraint( Node::ASAP );
sm = p.createScheduleManager( "T1 MustStartOn, T2 ASAP" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->startTime(), task1->mustStartOn() );
QCOMPARE( task2->startTime(), et - Duration( 0, 16, 0 ) );
s = "Schedule backwards, T1 MustStartOn, T2 StartNotEarlier -------";
task2->setConstraint( Node::StartNotEarlier );
task2->setConstraintStartTime( task1->mustStartOn().addDays( -1 ) );
sm = p.createScheduleManager( "T1 MustStartOn, T2 StartNotEarlier" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->startTime(), task1->mustStartOn() );
QCOMPARE( task2->startTime(), et - Duration( 0, 16, 0 ) );
task2->setConstraintStartTime( task1->mustStartOn() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->startTime(), task1->mustStartOn() );
QCOMPARE( task2->startTime(), et - Duration( 0, 16, 0 ) );
task2->setConstraintStartTime( task1->mustStartOn().addDays( 1 ) );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->startTime(), task1->mustStartOn() );
QCOMPARE( task2->startTime(), et - Duration( 0, 16, 0 ) );
}
void ProjectTester::resourceConflictMustFinishOn()
{
Project p;
p.setName( "P1" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2012-02-01", Qt::ISODate );
st = DateTime( st.addDays( 1 ) );
st.setTime( QTime ( 0, 0, 0 ) );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *task1 = p.createTask();
task1->setName( "T1" );
task1->setConstraint( Node::MustFinishOn );
task1->setConstraintEndTime( st + Duration( 1, 16, 0 ) );
p.addSubTask( task1, &p );
task1->estimate()->setUnit( Duration::Unit_d );
task1->estimate()->setExpectedEstimate( 1.0 );
task1->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr = new ResourceGroupRequest( g );
task1->addRequest( gr );
ResourceRequest *tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
Task *task2 = p.createTask();
task2->setName( "T2" );
p.addSubTask( task2, &p );
task2->estimate()->setUnit( Duration::Unit_d );
task2->estimate()->setExpectedEstimate( 1.0 );
task2->estimate()->setType( Estimate::Type_Effort );
gr = new ResourceGroupRequest( g );
task2->addRequest( gr );
tr = new ResourceRequest( r1, 100 );
gr->addResourceRequest( tr );
QString s = "Schedule T1 MustFinishOn, T2 ASAP -------";
qDebug()<<s;
ScheduleManager *sm = p.createScheduleManager( "T1 MustFinishOn" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
s = "Schedule T1 MustFinishOn, T2 ALAP -------";
qDebug()<<s;
task2->setConstraint( Node::ALAP );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------";
qDebug()<<s;
task2->setConstraint( Node::StartNotEarlier );
task2->setConstraintStartTime( p.constraintStartTime() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------";
qDebug()<<s;
task2->setConstraintStartTime( task1->mustFinishOn() - Duration( 0, 8, 0 ) );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), task2->constraintStartTime() + Duration( 1, 0, 0 ) );
s = "Schedule T1 MustFinishOn, T2 StartNotEarlier -------";
qDebug()<<s;
task2->setConstraintStartTime( task1->mustFinishOn() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), task2->constraintStartTime() + Duration( 0, 16, 0 ) );
s = "Schedule backwards, T1 MustFinishOn, T2 ASAP -------";
qDebug()<<s;
task2->setConstraint( Node::ASAP );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->startTime(), task1->mustFinishOn() + Duration( 0, 16, 0 ) );
s = "Schedule backwards, T1 MustFinishOn, T2 ALAP -------";
qDebug()<<s;
DateTime et = p.constraintEndTime();
task2->setConstraint( Node::ALAP );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->endTime(), et - Duration( 0, 8, 0 ) );
s = "Schedule backwards, T1 MustFinishOn, T2 StartNotEarlier -------";
qDebug()<<s;
task2->setConstraint( Node::StartNotEarlier );
task2->setConstraintStartTime( task1->mustFinishOn() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->endTime(), et - Duration( 0, 8, 0 ) );
task2->setConstraintStartTime( p.constraintStartTime() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->endTime(), et - Duration( 0, 8, 0 ) );
s = "Schedule backwards, T1 MustFinishOn, T2 FinishNotLater -------";
qDebug()<<s;
task2->setConstraint( Node::FinishNotLater );
task2->setConstraintEndTime( task1->mustFinishOn() );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->endTime(), task1->startTime() - Duration( 0, 16, 0 ) );
task2->setConstraintEndTime( task1->mustFinishOn().addDays( 2 ) );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
// Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->endTime(), task1->mustFinishOn() );
QCOMPARE( task2->endTime(), task2->finishNotLater() );
}
void ProjectTester::fixedInterval()
{
Project p;
p.setName( "P1" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = DateTime::fromString( "2010-10-20T08:00", KDateTime::LocalZone );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *task1 = p.createTask();
task1->setName( "T1" );
task1->setConstraint( Node::FixedInterval );
task1->setConstraintStartTime( st );
task1->setConstraintEndTime( st.addDays( 1 ) );
p.addTask( task1, &p );
QString s = "Schedule T1 Fixed interval -------";
qDebug()<<s;
ScheduleManager *sm = p.createScheduleManager( "T1 Fixed interval" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), task1->constraintStartTime() );
QCOMPARE( task1->endTime(), task1->constraintEndTime() );
s = "Schedule backward: T1 Fixed interval -------";
qDebug()<<s;
sm->setSchedulingDirection( true );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
Debug::printSchedulingLog( *sm, s );
QCOMPARE( task1->startTime(), task1->constraintStartTime() );
QCOMPARE( task1->endTime(), task1->constraintEndTime() );
}
void ProjectTester::estimateDuration()
{
Project p;
p.setName( "P1" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2010-10-20 08:00" );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Task *task1 = p.createTask();
task1->setName( "T1" );
task1->setConstraint( Node::ASAP );
p.addTask( task1, &p );
task1->estimate()->setType( Estimate::Type_Duration );
task1->estimate()->setUnit( Duration::Unit_h );
task1->estimate()->setExpectedEstimate( 10 );
QString s = "Schedule T1 Estimate type Duration -------";
qDebug()<<s;
ScheduleManager *sm = p.createScheduleManager( "T1 Duration" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() );
QCOMPARE( task1->endTime(), task1->startTime() + Duration( 0, 10, 0 ) );
s = "Schedule backward: T1 Estimate type Duration -------";
qDebug()<<s;
sm->setSchedulingDirection( true );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->endTime(), p.constraintEndTime() );
QCOMPARE( task1->startTime(), task1->endTime() - Duration( 0, 10, 0 ) );
}
void ProjectTester::startStart()
{
Project p;
p.setName( "P1" );
p.setId( p.uniqueNodeId() );
p.registerNodeId( &p );
DateTime st = QDateTime::fromString( "2010-10-20 00:00:00", Qt::ISODate );
p.setConstraintStartTime( st );
p.setConstraintEndTime( st.addDays( 5 ) );
Calendar *c = new Calendar("Test");
QTime t1(8,0,0);
int length = 8*60*60*1000; // 8 hours
for ( int i = 1; i <= 7; ++i ) {
CalendarDay *wd1 = c->weekday(i);
wd1->setState(CalendarDay::Working);
wd1->addInterval(TimeInterval(t1, length));
}
p.addCalendar( c );
p.setDefaultCalendar( c );
ResourceGroup *g = new ResourceGroup();
p.addResourceGroup( g );
Resource *r1 = new Resource();
r1->setName( "R1" );
p.addResource( g, r1 );
Resource *r2 = new Resource();
r2->setName( "R2" );
p.addResource( g, r2 );
Task *task1 = p.createTask();
task1->setName( "T1" );
task1->setConstraint( Node::ASAP );
p.addTask( task1, &p );
task1->estimate()->setType( Estimate::Type_Duration );
task1->estimate()->setUnit( Duration::Unit_h );
task1->estimate()->setExpectedEstimate( 2 );
Task *task2 = p.createTask();
task2->setName( "T2" );
task2->setConstraint( Node::ASAP );
p.addTask( task2, &p );
task2->estimate()->setType( Estimate::Type_Duration );
task2->estimate()->setUnit( Duration::Unit_h );
task2->estimate()->setExpectedEstimate( 2 );
task1->addDependChildNode( task2, Relation::StartStart );
QString s = "Schedule T1 Lag = 0 -------";
qDebug()<<s;
ScheduleManager *sm = p.createScheduleManager( "Lag = 0" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() );
QCOMPARE( task1->lateStart(), task2->lateStart() );
QCOMPARE( task1->startTime(), task2->startTime() );
s = "Schedule backward T1 Lag = 0 -------";
qDebug()<<s;
sm = p.createScheduleManager( "Backward, Lag = 0" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::printSchedulingLog(*sm, s);
Debug::print( &p, s, true );
qDebug()<<"predeccessors:"<<task2->dependParentNodes();
QCOMPARE( task2->endTime(), p.constraintEndTime() );
QCOMPARE( task1->lateStart(), task2->lateStart() );
QCOMPARE( task1->startTime(), task2->startTime() );
s = "Schedule T1 calendar -------";
qDebug()<<s;
task1->estimate()->setCalendar( c );
sm = p.createScheduleManager( "Lag = 0" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task1->lateStart(), task2->lateStart() );
QCOMPARE( task1->startTime(), task2->startTime() );
s = "Schedule backward T1 calendar -------";
qDebug()<<s;
task1->estimate()->setCalendar( 0 );
task2->estimate()->setCalendar( c );
sm = p.createScheduleManager( "Backward, calendar, Lag = 0" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task2->endTime(), p.constraintEndTime() - Duration( 0, 8, 0 ) );
QCOMPARE( task1->lateStart(), task2->lateStart() );
QCOMPARE( task1->startTime(), task2->startTime() );
s = "Schedule Lag = 1 hour -------";
qDebug()<<s;
task1->estimate()->setCalendar( c );
task2->estimate()->setCalendar( 0 );
task1->dependChildNodes().at( 0 )->setLag( Duration( 0, 1, 0 ) );
sm = p.createScheduleManager( "Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QCOMPARE( task2->startTime(), task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
s = "Schedule backward Lag = 1 hour -------";
qDebug()<<s;
task1->estimate()->setCalendar( 0 );
task2->estimate()->setCalendar( c );
sm = p.createScheduleManager( "Backward, Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task2->endTime(), p.constraintEndTime() - Duration( 0, 8, 0 ) );
QCOMPARE( task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QCOMPARE( task2->startTime(), task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
s = "Schedule resources Lag = 1 hour -------";
qDebug()<<s;
task1->estimate()->setCalendar( 0 );
task2->estimate()->setCalendar( 0 );
ResourceGroupRequest *gr1 = new ResourceGroupRequest( g );
task1->addRequest( gr1 );
ResourceRequest *rr1 = new ResourceRequest( r1, 100 );
gr1->addResourceRequest( rr1 );
task1->estimate()->setType( Estimate::Type_Effort );
ResourceGroupRequest *gr2 = new ResourceGroupRequest( g );
task2->addRequest( gr2 );
ResourceRequest *rr2 = new ResourceRequest( r2, 100 );
gr2->addResourceRequest( rr2 );
task2->estimate()->setType( Estimate::Type_Effort );
sm = p.createScheduleManager( "Resources, Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( false );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() + Duration( 0, 8, 0 ) );
QCOMPARE( task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QCOMPARE( task2->startTime(), task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
s = "Schedule backward resources Lag = 1 hour -------";
qDebug()<<s;
sm = p.createScheduleManager( "Resources backward, Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task2->endTime(), p.constraintEndTime() - Duration( 0, 8, 0 ) );
QCOMPARE( task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QCOMPARE( task2->startTime(), task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
s = "Schedule resources w limited availability, Lag = 1 hour -------";
qDebug()<<s;
r1->setAvailableFrom( p.constraintStartTime() + Duration( 0, 9, 0 ) );
r1->setAvailableUntil( p.constraintEndTime() - Duration( 0, 12, 0 ) );
sm = p.createScheduleManager( "Resources, Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( false );
p.calculate( *sm );
Debug::print( &p, s, true );
QCOMPARE( task1->startTime(), p.constraintStartTime() + Duration( 0, 9, 0 ) );
QCOMPARE( task2->lateStart(), task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QCOMPARE( task2->startTime(), task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
s = "Schedule backward resources w limited availability Lag = 1 hour -------";
qDebug()<<s;
sm = p.createScheduleManager( "Resources backward, Lag = 1 hour" );
p.addScheduleManager( sm );
sm->createSchedules();
sm->setSchedulingDirection( true );
p.calculate( *sm );
Debug::print( &p, s, true );
QVERIFY( task2->lateStart() >= task1->lateStart() + task1->dependChildNodes().at( 0 )->lag() );
QVERIFY( task2->startTime() >= task1->startTime() + task1->dependChildNodes().at( 0 )->lag() );
}
} //namespace KPlato
QTEST_GUILESS_MAIN( KPlato::ProjectTester )
diff --git a/plan/libs/kernel/tests/ProjectTester.h b/plan/libs/kernel/tests/ProjectTester.h
index 3a6981b13c2..ddd933eed40 100644
--- a/plan/libs/kernel/tests/ProjectTester.h
+++ b/plan/libs/kernel/tests/ProjectTester.h
@@ -1,84 +1,84 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ProjectTester_h
#define KPlato_ProjectTester_h
-#include <QtTest>
+#include <QObject>
#include "kptproject.h"
#include "kptdatetime.h"
#include <ktempdir.h>
namespace KPlato
{
class Task;
class ProjectTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testAddTask();
void testTakeTask();
void testTaskAddCmd();
void testTaskDeleteCmd();
void schedule();
void scheduleFullday();
void scheduleFulldayDstSpring();
void scheduleFulldayDstFall();
void scheduleWithExternalAppointments();
void reschedule();
void materialResource();
void requiredResource();
void resourceWithLimitedAvailability();
void unavailableResource();
void team();
// NOTE: It's not *mandatory* to schedule in wbs order but users expect it, so we'll try.
// This test can be removed if for some important reason this isn't possible.
void inWBSOrder();
void resourceConflictALAP();
void resourceConflictMustStartOn();
void resourceConflictMustFinishOn();
void fixedInterval();
void estimateDuration();
void startStart();
private:
Project *m_project;
Calendar *m_calendar;
Task *m_task;
KTempDir m_tmp;
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/ResourceTester.h b/plan/libs/kernel/tests/ResourceTester.h
index 6e2419e9a9e..ab5d5f9cc88 100644
--- a/plan/libs/kernel/tests/ResourceTester.h
+++ b/plan/libs/kernel/tests/ResourceTester.h
@@ -1,40 +1,40 @@
/* This file is part of the KDE project
Copyright (C) 2009 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ResourceTester_h
#define KPlato_ResourceTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class ResourceTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testAvailable();
void testSingleDay();
void team();
void required();
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/ScheduleTester.h b/plan/libs/kernel/tests/ScheduleTester.h
index 23b4758110c..fbca70646a6 100644
--- a/plan/libs/kernel/tests/ScheduleTester.h
+++ b/plan/libs/kernel/tests/ScheduleTester.h
@@ -1,56 +1,56 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ScheduleTester_h
#define KPlato_ScheduleTester_h
-#include <QtTest>
+#include <QObject>
#include <QDate>
#include <QTime>
#include "kptschedule.h"
#include "kptdatetime.h"
namespace KPlato
{
class ScheduleTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void available();
void busy();
private:
ResourceSchedule resourceSchedule;
NodeSchedule nodeSchedule;
QDate date;
QTime t1;
QTime t2;
QTime t3;
};
} //namespace KPlato
#endif
diff --git a/plan/libs/kernel/tests/WorkInfoCacheTester.h b/plan/libs/kernel/tests/WorkInfoCacheTester.h
index 3436f07d56f..fb4d5d54771 100644
--- a/plan/libs/kernel/tests/WorkInfoCacheTester.h
+++ b/plan/libs/kernel/tests/WorkInfoCacheTester.h
@@ -1,48 +1,48 @@
/* This file is part of the KDE project
Copyright (C) 2012 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_WorkInfoCacheTester_h
#define KPlato_WorkInfoCacheTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class WorkInfoCacheTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void basics();
void addAfter();
void addBefore();
void addMiddle();
void fullDay();
void timeZone();
private:
void removeDir(const QString &dir);
};
} //namespace KPlato
#endif
diff --git a/plan/libs/models/tests/FlatProxyModelTester.h b/plan/libs/models/tests/FlatProxyModelTester.h
index ee7a511c00c..15dff6fbc81 100644
--- a/plan/libs/models/tests/FlatProxyModelTester.h
+++ b/plan/libs/models/tests/FlatProxyModelTester.h
@@ -1,51 +1,51 @@
/* This file is part of the KDE project
Copyright (C) 2010 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_FlatProxyModelTester_h
#define KPlato_FlatProxyModelTester_h
-#include <QtTest>
+#include <QObject>
#include "kptflatproxymodel.h"
#include <QStandardItemModel>
namespace KPlato
{
class FlatProxyModelTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void emptyModel();
void test();
void testInsertRemoveTop();
void testInsertRemoveChildren();
void testInsertRemoveGrandChildren();
private:
FlatProxyModel m_flatmodel;
QStandardItemModel m_standardmodel;
};
} //namespace KPlato
#endif
diff --git a/plan/libs/models/tests/ResourceModelTester.h b/plan/libs/models/tests/ResourceModelTester.h
index 928689a0cfb..bbf206e8a9b 100644
--- a/plan/libs/models/tests/ResourceModelTester.h
+++ b/plan/libs/models/tests/ResourceModelTester.h
@@ -1,61 +1,61 @@
/* This file is part of the KDE project
Copyright (C) 2009 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ResourceModelTester_h
#define KPlato_ResourceModelTester_h
-#include <QtTest>
+#include <QObject>
#include "kptresourceappointmentsmodel.h"
#include "kptproject.h"
#include "kptdatetime.h"
namespace KPlato
{
class Task;
class ResourceModelTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void internalAppointments();
void externalAppointments();
void externalOverbook();
private:
void printDebug( long id ) const;
void printSchedulingLog( const ScheduleManager &sm ) const;
Project *m_project;
Calendar *m_calendar;
Task *m_task;
Resource *m_resource;
ResourceAppointmentsGanttModel m_model;
};
} //namespace KPlato
#endif
diff --git a/plan/libs/models/tests/WorkPackageProxyModelTester.h b/plan/libs/models/tests/WorkPackageProxyModelTester.h
index e452b189448..5fdd3def3ce 100644
--- a/plan/libs/models/tests/WorkPackageProxyModelTester.h
+++ b/plan/libs/models/tests/WorkPackageProxyModelTester.h
@@ -1,58 +1,58 @@
/* This file is part of the KDE project
Copyright (C) 2012 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_WorkPackageProxyModelTester_h
#define KPlato_WorkPackageProxyModelTester_h
-#include <QtTest>
+#include <QObject>
#include "kptworkpackagemodel.h"
#include "kptproject.h"
namespace KPlato
{
class Task;
class ScheduleManager;
class WorkPackageProxyModelTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testInsert();
void testRemove();
void testMove();
void testMoveChild();
private:
Task *addTask( Node *parent, int after );
void removeTask( Node *task );
void moveTask( Node *task, Node *newParent, int newPos );
private:
WorkPackageProxyModel m_model;
Project project;
ScheduleManager *sm;
};
} //namespace KPlato
#endif
diff --git a/plan/plugins/schedulers/rcps/tests/ProjectTester.h b/plan/plugins/schedulers/rcps/tests/ProjectTester.h
index d192a472e92..074df9ee55d 100644
--- a/plan/plugins/schedulers/rcps/tests/ProjectTester.h
+++ b/plan/plugins/schedulers/rcps/tests/ProjectTester.h
@@ -1,59 +1,59 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ProjectTester_h
#define KPlato_ProjectTester_h
-#include <QtTest>
+#include <QObject>
#include <ktempdir.h>
#include "kptproject.h"
#include "kptdatetime.h"
namespace KPlato
{
class Task;
class ProjectTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void oneTask();
void team();
void mustStartOn();
void startNotEarlier();
private:
void initTimezone();
void cleanupTimezone();
KTempDir m_tmp;
Project *m_project;
Calendar *m_calendar;
Task *m_task;
};
} //namespace KPlato
#endif
diff --git a/plan/plugins/schedulers/tj/tests/SchedulerTester.h b/plan/plugins/schedulers/tj/tests/SchedulerTester.h
index 4e33791035c..034ca0c3139 100644
--- a/plan/plugins/schedulers/tj/tests/SchedulerTester.h
+++ b/plan/plugins/schedulers/tj/tests/SchedulerTester.h
@@ -1,51 +1,51 @@
/* This file is part of the KDE project
Copyright (C) 2011 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_SchedulerTester_h
#define KPlato_SchedulerTester_h
-#include <QtTest>
+#include <QObject>
class KoXmlDocument;
namespace KPlato
{
class Node;
class SchedulerTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void test();
private:
void initTimezone();
void cleanupTimezone();
void removeDir(const QString &dir);
QStringList data();
void testProject(const QString &fname, const KoXmlDocument &doc );
void compare( const QString &fname, Node *n, long id1, long id2 );
};
} //namespace KPlato
#endif
diff --git a/plan/plugins/schedulers/tj/tests/TaskJuggler.h b/plan/plugins/schedulers/tj/tests/TaskJuggler.h
index bbabcf91aa5..3595f512537 100644
--- a/plan/plugins/schedulers/tj/tests/TaskJuggler.h
+++ b/plan/plugins/schedulers/tj/tests/TaskJuggler.h
@@ -1,64 +1,64 @@
/* This file is part of the KDE project
Copyright (C) 2011 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_TaskJuggler_h
#define KPlato_TaskJuggler_h
-#include <QtTest>
+#include <QObject>
#include <ktempdir.h>
namespace TJ {
class Project;
}
namespace KPlato
{
class TaskJuggler : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void list();
void projectTest();
void oneTask();
void oneResource();
void allocation();
void dependency();
void scheduleResource();
void scheduleDependencies();
void scheduleConstraints();
void resourceConflict();
void units();
private:
void initTimezone();
void cleanupTimezone();
private:
KTempDir m_tmp;
TJ::Project *project;
};
} //namespace KPlato
#endif
diff --git a/plan/plugins/scripting/tests/ScriptingTester.h b/plan/plugins/scripting/tests/ScriptingTester.h
index 4cb868c0881..b850489e97c 100644
--- a/plan/plugins/scripting/tests/ScriptingTester.h
+++ b/plan/plugins/scripting/tests/ScriptingTester.h
@@ -1,60 +1,60 @@
/* This file is part of the KDE project
Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_ScriptingTester_h
#define KPlato_ScriptingTester_h
-#include <QtTest>
+#include <QObject>
class TestResult;
namespace Kross {
class Action;
}
namespace Scripting {
class Module;
}
namespace KPlato
{
class ScriptingTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void test();
private:
QStringList initTestList();
private:
QList<Kross::Action*> m_tests;
Scripting::Module *m_module;
TestResult *m_result;
};
} //namespace KPlato
#endif
diff --git a/plan/tests/InsertFileTester.h b/plan/tests/InsertFileTester.h
index b53e71b13b3..7af62622ed4 100644
--- a/plan/tests/InsertFileTester.h
+++ b/plan/tests/InsertFileTester.h
@@ -1,41 +1,41 @@
/* This file is part of the KDE project
Copyright (C) 2009 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_InsertFileTester_h
#define KPlato_InsertFileTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class InsertFileTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testVersion_0_6();
void testProject_stats1();
void testPert1();
};
}
#endif
diff --git a/plan/tests/InsertProjectTester.h b/plan/tests/InsertProjectTester.h
index 835c3c767ae..6641e1ec008 100644
--- a/plan/tests/InsertProjectTester.h
+++ b/plan/tests/InsertProjectTester.h
@@ -1,72 +1,72 @@
/* This file is part of the KDE project
Copyright (C) 2009 Dag Andersen <danders@get2net.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KPlato_InsertProjectTester_h
#define KPlato_InsertProjectTester_h
-#include <QtTest>
+#include <QObject>
namespace KPlato
{
class MainDocument;
class Account;
class ResourceGroup;
class Resource;
class Task;
class Relation;
class Calendar;
class InsertProjectTester : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testAccount();
void testCalendar();
void testDefaultCalendar();
void testResourceGroup();
void testResource();
void testTeamResource();
void testResourceAccount();
void testResourceCalendar();
void testTask();
void testGroupRequest();
void testResourceRequest();
void testTeamResourceRequest();
void testDependencies();
void testExistingResourceAccount();
void testExistingResourceCalendar();
void testExistingResourceRequest();
void testExistingRequiredResourceRequest();
void testExistingTeamResourceRequest();
private:
Account *addAccount( MainDocument &part, Account *parent = 0 );
Calendar *addCalendar( MainDocument &part );
ResourceGroup *addResourceGroup( MainDocument &part );
Resource *addResource( MainDocument &part, ResourceGroup *g = 0 );
Task *addTask( MainDocument &part );
void addGroupRequest( MainDocument &part );
void addResourceRequest( MainDocument &part );
Relation *addDependency( MainDocument &part, Task *t1, Task *t2 );
};
}
#endif
diff --git a/plugins/chartshape/tests/TestCellRegion.cpp b/plugins/chartshape/tests/TestCellRegion.cpp
index 37fe0841497..b5a033be851 100644
--- a/plugins/chartshape/tests/TestCellRegion.cpp
+++ b/plugins/chartshape/tests/TestCellRegion.cpp
@@ -1,160 +1,160 @@
/* This file is part of the KDE project
Copyright (C) 2010 Johannes Simon <johannes.simon@gmail.com>
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
Contact: Suresh Chande suresh.chande@nokia.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// Own
#include "TestCellRegion.h"
// Qt
-#include <QtTest>
+#include <QTest>
#include <QDebug>
// KoChart
#include "CellRegion.h"
TestCellRegion::TestCellRegion()
: QObject(0)
{
}
void TestCellRegion::init()
{
m_source.clear();
m_source.add("Table1", &m_model1);
m_source.add("Table2", &m_model2);
Table *t1 = m_source.get("Table1");
Table *t2 = m_source.get("Table2");
m_region1 = CellRegion(t1);
m_region1.add(CellRegion(t1, QRect(2, 3, 10, 11)));
m_region2 = CellRegion();
m_region2.add(CellRegion(t1, QRect(2, 3, 10, 11)));
m_region2.add(CellRegion(t2, QRect(1, 2, 5, 6)));
}
void TestCellRegion::testToStringSingleTable()
{
QCOMPARE(m_region1.toString(), QString("$Table1.$B$3:$K$13"));
}
void TestCellRegion::testSkippedTableEntry()
{
const CellRegion region(&m_source, QString("Table1.$A$3:.$C$3"));
QVector< QRect > rects;
rects.append(QRect(QPoint(1, 3), QPoint(3, 3)));
QCOMPARE(region.rects(), rects);
}
void TestCellRegion::testFromStringSingleTable()
{
QCOMPARE(m_region1, CellRegion(&m_source, "$Table1.$B$3:$K$13"));
}
void TestCellRegion::testTableNameChangeSingleTable()
{
m_source.rename("Table1", "DoubleBubbleGumBubblesDouble");
QCOMPARE(m_region1.toString(), QString("$DoubleBubbleGumBubblesDouble.$B$3:$K$13"));
}
void TestCellRegion::testToStringWithSpecialCharactersSingleTable()
{
m_source.rename("Table1", "table-one");
QCOMPARE(m_region1.toString(), QString("$'table-one'.$B$3:$K$13"));
}
void TestCellRegion::testFromStringWithSpecialCharactersSingleTable()
{
m_source.rename("Table1", "table-one");
CellRegion region(&m_source, QString("$'table-one'.$B$3:$K$13"));
QCOMPARE(region.table(), m_source.get("table-one"));
}
void TestCellRegion::testListOfRegions()
{
CellRegion region(&m_source, QString("$Table1.$A$1:$F$13 $Table1.$A$15:$F$26"));
QCOMPARE(region.table(), m_source.get("Table1"));
const QVector< QRect > rects = region.rects();
QVector< QRect > compareRects;
compareRects.push_back(QRect(QPoint(1, 1), QPoint(6, 13)));
compareRects.push_back(QRect(QPoint(1, 15), QPoint(6, 26)));
QCOMPARE(rects.count(), compareRects.count());
for (int i = 0; i < compareRects.count() && i < rects.count(); ++i)
{
QCOMPARE(rects[i], compareRects[i]);
}
}
void TestCellRegion::testListOfRegions2()
{
CellRegion region(&m_source, QString("Table1.A19:Table1.A30 Table1.E20:Table1.E30 Table1.G20:Table1.G30 Table1.I20:Table1.I30 Table1.E58:Table1.E71"));
QCOMPARE(region.table(), m_source.get("Table1"));
const QVector< QRect > rects = region.rects();
QVector< QRect > compareRects;
compareRects.push_back(QRect(QPoint(1, 19), QPoint(1, 30)));
compareRects.push_back(QRect(QPoint(5, 20), QPoint(5, 30)));
compareRects.push_back(QRect(QPoint(7,20), QPoint(7, 30)));
compareRects.push_back(QRect(QPoint(9,20), QPoint(9, 30)));
compareRects.push_back(QRect(QPoint(5,58), QPoint(5, 71)));
for (int i = 0; i < compareRects.count() && i < rects.count(); ++i)
{
QCOMPARE(rects[i], compareRects[i]);
}
}
void TestCellRegion::testToStringMultipleTables()
{
QEXPECT_FAIL("", "Functionality is not yet supported, so its expected to fail", Continue);
QCOMPARE(m_region2.toString(), QString("$Table1.$B$3:$K$13;$Table2.$A$2:$E$7"));
}
void TestCellRegion::testFromStringMultipleTables()
{
//QEXPECT_FAIL("", "Functionality is not yet supported, so its expected to fail", Continue);
QCOMPARE(m_region2, CellRegion(&m_source, "$Table1.$B$3:$K$13;$Table2.$A$2:$E$7"));
}
void TestCellRegion::testTableNameChangeMultipleTables()
{
m_source.rename("Table1", "AGoodCookCanCookGoodCookies");
QEXPECT_FAIL("", "Functionality is not yet supported, so its expected to fail", Continue);
QCOMPARE(m_region2.toString(), QString("$AGoodCookCanCookGoodCookies.$B$3:$K$13;$Table2.$A$2:$E$7"));
m_source.rename("Table2", "DoubleBubbleGumBubblesDouble");
QEXPECT_FAIL("", "Functionality is not yet supported, so its expected to fail", Continue);
QCOMPARE(m_region2.toString(), QString("$AGoodCookCanCookGoodCookies.$B$3:$K$13;$DoubleBubbleGumBubblesDouble.$A$2:$E$7"));
}
void TestCellRegion::testToStringWithSpecialCharactersMultipleTables()
{
m_source.rename("Table1", "table-one");
QEXPECT_FAIL("", "Functionality is not yet supported, so its expected to fail", Continue);
QCOMPARE(m_region2.toString(), QString("$'table-one'.$B$3:$K$13;$Table2.$A$2:$E$7"));
}
void TestCellRegion::testFromStringWithSpecialCharactersMultipleTables()
{
m_source.rename("Table1", "table-one");
CellRegion region(&m_source, QString("$'table-one'.$B$3:$K$13"));
QCOMPARE(region.table(), m_source.get("table-one"));
}
QTEST_MAIN(TestCellRegion)
diff --git a/plugins/chartshape/tests/TestDataSet.cpp b/plugins/chartshape/tests/TestDataSet.cpp
index 1b261ddbe79..32b7fa71462 100644
--- a/plugins/chartshape/tests/TestDataSet.cpp
+++ b/plugins/chartshape/tests/TestDataSet.cpp
@@ -1,195 +1,196 @@
/* This file is part of the KDE project
Copyright 2008 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestDataSet.h"
// Qt
#include <QRect>
#include <QPoint>
#include <QSize>
#include <QDebug>
#include <QString>
#include <QVariant>
+#include <QTest>
// KoChart
#include "DataSet.h"
#include "CellRegion.h"
namespace QTest {
template<>
char *toString(const CellRegion &region)
{
return qstrdup(region.toString().toLatin1());
}
}
using namespace KoChart;
TestDataSet::TestDataSet()
: m_source()
, m_proxyModel(0, &m_source)
, m_sourceModel1()
, m_table1(0)
{
}
void TestDataSet::initTestCase()
{
m_table1 = m_source.add("Table1", &m_sourceModel1);
m_table2 = m_source.add("Table2", &m_sourceModel2);
m_sourceModel1.setRowCount(4);
m_sourceModel1.setColumnCount(5);
// Vertical header data
m_sourceModel1.setData(m_sourceModel1.index(1, 0), "Row 1");
m_sourceModel1.setData(m_sourceModel1.index(2, 0), "Row 2");
m_sourceModel1.setData(m_sourceModel1.index(3, 0), "Row 3");
// Horizontal header data
m_sourceModel1.setData(m_sourceModel1.index(0, 1), "Column 1");
m_sourceModel1.setData(m_sourceModel1.index(0, 2), "Column 2");
m_sourceModel1.setData(m_sourceModel1.index(0, 3), "Column 3");
m_sourceModel1.setData(m_sourceModel1.index(0, 4), "Column 4");
// First row
m_sourceModel1.setData(m_sourceModel1.index(1, 1), 7.2);
m_sourceModel1.setData(m_sourceModel1.index(1, 2), 1.8);
m_sourceModel1.setData(m_sourceModel1.index(1, 3), 9.4);
m_sourceModel1.setData(m_sourceModel1.index(1, 4), 1.5);
// Second row
m_sourceModel1.setData(m_sourceModel1.index(2, 1), 8.4);
m_sourceModel1.setData(m_sourceModel1.index(2, 2), 2.9);
m_sourceModel1.setData(m_sourceModel1.index(2, 3), 3.7);
m_sourceModel1.setData(m_sourceModel1.index(2, 4), 5.5);
// Third row
m_sourceModel1.setData(m_sourceModel1.index(3, 1), 2.9);
m_sourceModel1.setData(m_sourceModel1.index(3, 2), 5.3);
m_sourceModel1.setData(m_sourceModel1.index(3, 3), 6.4);
m_sourceModel1.setData(m_sourceModel1.index(3, 4), 2.1);
m_sourceModel2.setRowCount(4);
m_sourceModel2.setColumnCount(5);
// Vertical header data
m_sourceModel2.setData(m_sourceModel2.index(1, 0), "Row 1");
m_sourceModel2.setData(m_sourceModel2.index(2, 0), "Row 2");
m_sourceModel2.setData(m_sourceModel2.index(3, 0), "Row 3");
// Horizontal header data
m_sourceModel2.setData(m_sourceModel2.index(0, 1), "Column 1");
m_sourceModel2.setData(m_sourceModel2.index(0, 2), "Column 2");
m_sourceModel2.setData(m_sourceModel2.index(0, 3), "Column 3");
m_sourceModel2.setData(m_sourceModel2.index(0, 4), "Column 4");
// First row
m_sourceModel2.setData(m_sourceModel2.index(1, 1), 1);
m_sourceModel2.setData(m_sourceModel2.index(1, 2), 2);
m_sourceModel2.setData(m_sourceModel2.index(1, 3), 3);
m_sourceModel2.setData(m_sourceModel2.index(1, 4), 4);
// Second row
m_sourceModel2.setData(m_sourceModel2.index(2, 1), 5);
m_sourceModel2.setData(m_sourceModel2.index(2, 2), 6);
m_sourceModel2.setData(m_sourceModel2.index(2, 3), 7);
m_sourceModel2.setData(m_sourceModel2.index(2, 4), 8);
// Third row
m_sourceModel2.setData(m_sourceModel2.index(3, 1), 9);
m_sourceModel2.setData(m_sourceModel2.index(3, 2), 10);
m_sourceModel2.setData(m_sourceModel2.index(3, 3), 11);
m_sourceModel2.setData(m_sourceModel2.index(3, 4), 12);
}
void TestDataSet::testFooData()
{
DataSet dataSet(0);
dataSet.setLabelDataRegion(CellRegion(m_table1, QPoint(1, 2)));
dataSet.setCategoryDataRegion(CellRegion(m_table1, QRect(2, 1, 4, 1)));
dataSet.setXDataRegion(CellRegion(m_table1, QRect(2, 2, 4, 1)));
dataSet.setYDataRegion(CellRegion(m_table1, QRect(2, 3, 4, 1)));
dataSet.setCustomDataRegion(CellRegion(m_table1, QRect(2, 4, 4, 1)));
QCOMPARE(dataSet.size(), 4);
QCOMPARE(dataSet.labelData(), QVariant("Row 1"));
QCOMPARE(dataSet.categoryData(0), QVariant("Column 1"));
QCOMPARE(dataSet.categoryData(1), QVariant("Column 2"));
QCOMPARE(dataSet.categoryData(2), QVariant("Column 3"));
QCOMPARE(dataSet.categoryData(3), QVariant("Column 4"));
QCOMPARE(dataSet.xData(0), QVariant(7.2));
QCOMPARE(dataSet.xData(1), QVariant(1.8));
QCOMPARE(dataSet.xData(2), QVariant(9.4));
QCOMPARE(dataSet.xData(3), QVariant(1.5));
QCOMPARE(dataSet.yData(0), QVariant(8.4));
QCOMPARE(dataSet.yData(1), QVariant(2.9));
QCOMPARE(dataSet.yData(2), QVariant(3.7));
QCOMPARE(dataSet.yData(3), QVariant(5.5));
QCOMPARE(dataSet.customData(0), QVariant(2.9));
QCOMPARE(dataSet.customData(1), QVariant(5.3));
QCOMPARE(dataSet.customData(2), QVariant(6.4));
QCOMPARE(dataSet.customData(3), QVariant(2.1));
}
void TestDataSet::testFooDataMultipleTables()
{
DataSet dataSet(0);
dataSet.setLabelDataRegion(CellRegion(m_table1, QPoint(1, 2)));
dataSet.setCategoryDataRegion(CellRegion(m_table1, QRect(2, 1, 4, 1)));
dataSet.setXDataRegion(CellRegion(m_table2, QRect(2, 2, 4, 1)));
dataSet.setYDataRegion(CellRegion(m_table1, QRect(2, 3, 4, 1)));
dataSet.setCustomDataRegion(CellRegion(m_table2, QRect(2, 4, 4, 1)));
QCOMPARE(dataSet.size(), 4);
QCOMPARE(dataSet.labelData(), QVariant("Row 1"));
QCOMPARE(dataSet.categoryData(0), QVariant("Column 1"));
QCOMPARE(dataSet.categoryData(1), QVariant("Column 2"));
QCOMPARE(dataSet.categoryData(2), QVariant("Column 3"));
QCOMPARE(dataSet.categoryData(3), QVariant("Column 4"));
QCOMPARE(dataSet.xData(0), QVariant(1));
QCOMPARE(dataSet.xData(1), QVariant(2));
QCOMPARE(dataSet.xData(2), QVariant(3));
QCOMPARE(dataSet.xData(3), QVariant(4));
QCOMPARE(dataSet.yData(0), QVariant(8.4));
QCOMPARE(dataSet.yData(1), QVariant(2.9));
QCOMPARE(dataSet.yData(2), QVariant(3.7));
QCOMPARE(dataSet.yData(3), QVariant(5.5));
QCOMPARE(dataSet.customData(0), QVariant(9));
QCOMPARE(dataSet.customData(1), QVariant(10));
QCOMPARE(dataSet.customData(2), QVariant(11));
QCOMPARE(dataSet.customData(3), QVariant(12));
}
QTEST_MAIN(TestDataSet)
diff --git a/plugins/chartshape/tests/TestDataSet.h b/plugins/chartshape/tests/TestDataSet.h
index b4a584533b4..e54ecab8817 100644
--- a/plugins/chartshape/tests/TestDataSet.h
+++ b/plugins/chartshape/tests/TestDataSet.h
@@ -1,56 +1,53 @@
/* This file is part of the KDE project
Copyright 2008 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KCHART_TESTDATASET_H
#define KCHART_TESTDATASET_H
-// Qt
-#include <QtTest>
-
// KoChart
#include "ChartProxyModel.h"
#include "ChartTableModel.h"
#include "TableSource.h"
using namespace KoChart;
class TestDataSet : public QObject
{
Q_OBJECT
public:
TestDataSet();
private Q_SLOTS:
void initTestCase();
// Tests DataSet::*Data() methods
void testFooData();
void testFooDataMultipleTables();
private:
// m_source must be initialized before m_proxyModel
TableSource m_source;
ChartProxyModel m_proxyModel;
ChartTableModel m_sourceModel1, m_sourceModel2;
Table *m_table1, *m_table2;
};
#endif // KCHART_TESTDATASET_H
diff --git a/plugins/chartshape/tests/TestKChartModel.h b/plugins/chartshape/tests/TestKChartModel.h
index 8747c15da7d..f51dfd707f5 100644
--- a/plugins/chartshape/tests/TestKChartModel.h
+++ b/plugins/chartshape/tests/TestKChartModel.h
@@ -1,61 +1,61 @@
/* This file is part of the KDE project
Copyright 2009 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KCHART_TESTKDCHARTMODEL_H
#define KCHART_TESTKDCHARTMODEL_H
-#include <QtTest>
+#include <QTest>
#include <QStandardItemModel>
#include "KChartModel.h"
#include "ModelObserver.h"
#include "TableSource.h"
using namespace KoChart;
class TestKChartModel : public QObject
{
Q_OBJECT
public:
TestKChartModel();
private Q_SLOTS:
void init();
void cleanup();
void initTestCase();
void testDataSetInsertion();
void testDataSetInsertionAndRemoval();
void testData();
void testDataChanges();
void testDataChangesWithTwoDimensions();
private:
KChartModel *m_model;
ModelObserver *m_testModel;
// These are all only dummies, but we need them for valid CellRegions
QStandardItemModel m_itemModel;
TableSource m_source;
Table *m_table;
};
#endif // KCHART_TESTKDCHARTMODEL_H
diff --git a/plugins/chartshape/tests/TestProxyModel.cpp b/plugins/chartshape/tests/TestProxyModel.cpp
index a05f9a1e253..9607b0630aa 100644
--- a/plugins/chartshape/tests/TestProxyModel.cpp
+++ b/plugins/chartshape/tests/TestProxyModel.cpp
@@ -1,603 +1,604 @@
/* This file is part of the KDE project
Copyright 2008, 2010 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestProxyModel.h"
// Qt
#include <QRect>
#include <QPoint>
#include <QSize>
#include <QDebug>
#include <QString>
#include <QVariant>
+#include <QTest>
// KoChart
#include "DataSet.h"
#include "CellRegion.h"
namespace QTest {
template<>
char *toString(const CellRegion &region)
{
return qstrdup(region.toString().toLatin1());
}
}
using namespace KoChart;
TestProxyModel::TestProxyModel()
: m_source()
, m_proxyModel(0, &m_source)
, m_sourceModel()
, m_table(0)
{
}
void TestProxyModel::init()
{
m_source.clear();
m_table = m_source.add("Table1", &m_sourceModel);
m_sourceModel.setRowCount(4);
m_sourceModel.setColumnCount(5);
// Vertical header data
m_sourceModel.setData(m_sourceModel.index(1, 0), "Row 1");
m_sourceModel.setData(m_sourceModel.index(2, 0), "Row 2");
m_sourceModel.setData(m_sourceModel.index(3, 0), "Row 3");
// Horizontal header data
m_sourceModel.setData(m_sourceModel.index(0, 1), "Column 1");
m_sourceModel.setData(m_sourceModel.index(0, 2), "Column 2");
m_sourceModel.setData(m_sourceModel.index(0, 3), "Column 3");
m_sourceModel.setData(m_sourceModel.index(0, 4), "Column 4");
// First row
m_sourceModel.setData(m_sourceModel.index(1, 1), 7.2);
m_sourceModel.setData(m_sourceModel.index(1, 2), 1.8);
m_sourceModel.setData(m_sourceModel.index(1, 3), 9.4);
m_sourceModel.setData(m_sourceModel.index(1, 4), 1.5);
// Second row
m_sourceModel.setData(m_sourceModel.index(2, 1), 8.4);
m_sourceModel.setData(m_sourceModel.index(2, 2), 2.9);
m_sourceModel.setData(m_sourceModel.index(2, 3), 3.7);
m_sourceModel.setData(m_sourceModel.index(2, 4), 5.5);
// Third row
m_sourceModel.setData(m_sourceModel.index(3, 1), 2.9);
m_sourceModel.setData(m_sourceModel.index(3, 2), 5.3);
m_sourceModel.setData(m_sourceModel.index(3, 3), 6.4);
m_sourceModel.setData(m_sourceModel.index(3, 4), 2.1);
QRect selection(QPoint(1, 1), QSize(m_sourceModel.columnCount(), m_sourceModel.rowCount()));
m_proxyModel.reset(CellRegion(m_table, selection));
}
void TestProxyModel::testWithoutLabels()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
m_proxyModel.setFirstRowIsLabel(false);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 4);
QCOMPARE(dataSets[0]->size(), 5);
QCOMPARE(dataSets[1]->size(), 5);
QCOMPARE(dataSets[2]->size(), 5);
QCOMPARE(dataSets[3]->size(), 5);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion());
// Vertical data direction
m_proxyModel.setDataDirection(Qt::Vertical);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 5);
QCOMPARE(dataSets[0]->size(), 4);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[2]->size(), 4);
QCOMPARE(dataSets[3]->size(), 4);
QCOMPARE(dataSets[4]->size(), 4);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 4)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 4)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 4)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 4)));
QCOMPARE(dataSets[4]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion());
}
void TestProxyModel::testFirstRowAsLabel()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
// With first row as category data
m_proxyModel.setFirstRowIsLabel(true);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 3);
QCOMPARE(dataSets[0]->size(), 5);
QCOMPARE(dataSets[1]->size(), 5);
QCOMPARE(dataSets[2]->size(), 5);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
// Vertical data direction
m_proxyModel.setDataDirection(Qt::Vertical);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 5);
QCOMPARE(dataSets[0]->size(), 3);
QCOMPARE(dataSets[1]->size(), 3);
QCOMPARE(dataSets[2]->size(), 3);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 1, 3)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 1)));
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(3, 2, 1, 3)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 1)));
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(4, 2, 1, 3)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 1)));
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(m_table, QRect(5, 2, 1, 3)));
QCOMPARE(dataSets[4]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 1)));
QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion());
}
void TestProxyModel::testFirstColumnAsLabel()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstRowIsLabel(false);
// With first column as label data
m_proxyModel.setFirstColumnIsLabel(true);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 4);
QCOMPARE(dataSets[0]->size(), 4);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[2]->size(), 4);
QCOMPARE(dataSets[3]->size(), 4);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 4, 1)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 1)));
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(2, 3, 4, 1)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(1, 3, 1, 1)));
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(2, 4, 4, 1)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(1, 4, 1, 1)));
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion());
// Vertical data direction
m_proxyModel.setDataDirection(Qt::Vertical);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 4);
QCOMPARE(dataSets[0]->size(), 4);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[2]->size(), 4);
QCOMPARE(dataSets[3]->size(), 4);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 4)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4)));
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 4)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4)));
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 4)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4)));
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 4)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion(m_table, QRect(1, 1, 1, 4)));
}
void TestProxyModel::testFirstRowAndColumnAsLabels()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
// With first row as category data
m_proxyModel.setFirstRowIsLabel(true);
// ...and first column as label data
m_proxyModel.setFirstColumnIsLabel(true);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 3);
QCOMPARE(dataSets[0]->size(), 4);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[2]->size(), 4);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 4, 1)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1)));
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(2, 3, 4, 1)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(1, 3, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1)));
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(2, 4, 4, 1)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(1, 4, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(2, 1, 4, 1)));
// Vertical data direction
m_proxyModel.setDataDirection(Qt::Vertical);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 4);
QCOMPARE(dataSets[0]->size(), 3);
QCOMPARE(dataSets[1]->size(), 3);
QCOMPARE(dataSets[2]->size(), 3);
QCOMPARE(dataSets[3]->size(), 3);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(2, 2, 1, 3)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion(m_table, QRect(2, 1, 1, 1)));
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3)));
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(3, 2, 1, 3)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion(m_table, QRect(3, 1, 1, 1)));
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3)));
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(4, 2, 1, 3)));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion(m_table, QRect(4, 1, 1, 1)));
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3)));
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(m_table, QRect(5, 2, 1, 3)));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion(m_table, QRect(5, 1, 1, 1)));
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion(m_table, QRect(1, 2, 1, 3)));
}
void TestProxyModel::testRegionOrder()
{
CellRegion selection(m_table);
// Second row
selection.add(QRect(1, 2, 4, 1));
// First row
selection.add(QRect(1, 1, 4, 1));
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
m_proxyModel.setFirstRowIsLabel(false);
m_proxyModel.reset(selection);
QList<DataSet*> dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 2);
QCOMPARE(dataSets[0]->size(), 4);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 1, 4, 1)));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 4, 1)));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
}
// Random test case with results from Open Office
void TestProxyModel::testComplexRegions()
{
CellRegion selection(&m_source, "Table1.C2:D3;Table1.D9:F10;Table1.E4:F4;Table1.E6:E7");
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
m_proxyModel.setFirstRowIsLabel(false);
m_proxyModel.reset(selection);
QList<DataSet*> dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 7);
QCOMPARE(dataSets[0]->size(), 2);
QCOMPARE(dataSets[1]->size(), 2);
QCOMPARE(dataSets[2]->size(), 2);
QCOMPARE(dataSets[3]->size(), 1);
QCOMPARE(dataSets[4]->size(), 1);
QCOMPARE(dataSets[5]->size(), 3);
QCOMPARE(dataSets[6]->size(), 3);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.C2:D2"));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.C3:D3"));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(&m_source, "Table1.E4:F4"));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->yDataRegion(), CellRegion(&m_source, "Table1.E6"));
QCOMPARE(dataSets[3]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[3]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->yDataRegion(), CellRegion(&m_source, "Table1.E7"));
QCOMPARE(dataSets[4]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[4]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[5]->yDataRegion(), CellRegion(&m_source, "Table1.D9:F9"));
QCOMPARE(dataSets[5]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[5]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[5]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[5]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[6]->yDataRegion(), CellRegion(&m_source, "Table1.D10:F10"));
QCOMPARE(dataSets[6]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[6]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[6]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[6]->categoryDataRegion(), CellRegion());
m_proxyModel.setDataDirection(Qt::Vertical);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 4);
QCOMPARE(dataSets[0]->size(), 2);
QCOMPARE(dataSets[1]->size(), 4);
QCOMPARE(dataSets[2]->size(), 5);
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.C2:C3"));
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.D2:D3;$Table1.D9:D10"));
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(&m_source, "Table1.E4;Table1.E6:E7;Table1.E9:E10"));
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
}
void TestProxyModel::testTwoDimensions()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
m_proxyModel.setFirstRowIsLabel(false);
m_proxyModel.setDataDimensions(2);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 3);
QCOMPARE(dataSets[0]->size(), 5);
QCOMPARE(dataSets[1]->size(), 5);
QCOMPARE(dataSets[2]->size(), 5);
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(m_table, QRect(1, 2, 5, 1)));
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(m_table, QRect(1, 3, 5, 1)));
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->xDataRegion(), CellRegion(m_table, QRect(1, 1, 5, 1)));
QCOMPARE(dataSets[2]->yDataRegion(), CellRegion(m_table, QRect(1, 4, 5, 1)));
QCOMPARE(dataSets[2]->customDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[2]->categoryDataRegion(), CellRegion());
}
void TestProxyModel::testThreeDimensions()
{
QList<DataSet*> dataSets;
// Horizontal data direction
m_proxyModel.setDataDirection(Qt::Horizontal);
m_proxyModel.setFirstColumnIsLabel(false);
m_proxyModel.setFirstRowIsLabel(false);
m_proxyModel.setDataDimensions(3);
dataSets = m_proxyModel.dataSets();
QCOMPARE(dataSets.size(), 2);
QCOMPARE(dataSets[0]->size(), 5);
QCOMPARE(dataSets[1]->size(), 5);
QCOMPARE(dataSets[0]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->yDataRegion(), CellRegion(&m_source, "Table1.$A$1:$E$1"));
QCOMPARE(dataSets[0]->customDataRegion(), CellRegion(&m_source, "Table1.$A$2:$E$2"));
QCOMPARE(dataSets[0]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[0]->categoryDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->xDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->yDataRegion(), CellRegion(&m_source, "Table1.$A$3:$E$3"));
QCOMPARE(dataSets[1]->customDataRegion(), CellRegion(&m_source, "Table1.$A$4:$E$4"));
QCOMPARE(dataSets[1]->labelDataRegion(), CellRegion());
QCOMPARE(dataSets[1]->categoryDataRegion(), CellRegion());
}
QTEST_MAIN(TestProxyModel)
diff --git a/plugins/chartshape/tests/TestProxyModel.h b/plugins/chartshape/tests/TestProxyModel.h
index b4fbca52ba6..22ac272871f 100644
--- a/plugins/chartshape/tests/TestProxyModel.h
+++ b/plugins/chartshape/tests/TestProxyModel.h
@@ -1,61 +1,58 @@
/* This file is part of the KDE project
Copyright 2008, 2010 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KCHART_TESTPROXYMODEL_H
#define KCHART_TESTPROXYMODEL_H
-// Qt
-#include <QtTest>
-
// KoChart
#include "ChartProxyModel.h"
#include "ChartTableModel.h"
#include "TableSource.h"
using namespace KoChart;
class TestProxyModel : public QObject
{
Q_OBJECT
public:
TestProxyModel();
private Q_SLOTS:
void init();
void testWithoutLabels();
void testFirstRowAsLabel();
void testFirstColumnAsLabel();
void testFirstRowAndColumnAsLabels();
void testRegionOrder();
void testComplexRegions();
void testTwoDimensions();
void testThreeDimensions();
private:
// m_source must be initialized before m_proxyModel
TableSource m_source;
ChartProxyModel m_proxyModel;
ChartTableModel m_sourceModel;
Table *m_table;
};
#endif // KCHART_TESTPROXYMODEL_H
diff --git a/plugins/chartshape/tests/TestTableSource.cpp b/plugins/chartshape/tests/TestTableSource.cpp
index 7c2a5bb1b99..7189c92ea3f 100644
--- a/plugins/chartshape/tests/TestTableSource.cpp
+++ b/plugins/chartshape/tests/TestTableSource.cpp
@@ -1,124 +1,124 @@
/* This file is part of the KDE project
Copyright 2010 Johannes Simon <johannes.simon@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestTableSource.h"
// Qt
#include <QList>
#include <QString>
-#include <QtTest>
+#include <QTest>
TestTableSource::TestTableSource()
: QObject()
{
}
void TestTableSource::init()
{
m_source.clear();
m_source.add("Table1", &m_table1);
m_source.add("Table2", &m_table2);
QStandardItem *table3 = new QStandardItem;
QStandardItem *table4 = new QStandardItem;
table3->setData(qVariantFromValue(QPointer<QAbstractItemModel>(&m_table3)),
Qt::DisplayRole);
table4->setData(qVariantFromValue(QPointer<QAbstractItemModel>(&m_table4)),
Qt::DisplayRole);
m_sheetAccessModel.setItem(0, 0, table3);
m_sheetAccessModel.setHeaderData(0, Qt::Horizontal, "Table3");
// Setting the sheetAccessModel now is done on purpose to test
// if already existent data is automatically used by table source
m_source.setSheetAccessModel(&m_sheetAccessModel);
m_sheetAccessModel.setItem(0, 1, table4);
m_sheetAccessModel.setHeaderData(1, Qt::Horizontal, "Table4");
}
void TestTableSource::testAdding()
{
QVERIFY(m_source.get("Table1"));
QCOMPARE(m_source.get("Table1")->model(), &m_table1);
QVERIFY(m_source.get("Table2"));
QCOMPARE(m_source.get("Table2")->model(), &m_table2);
}
void TestTableSource::testRenaming()
{
m_source.rename("Table2", "BlueBerryCake");
QVERIFY(m_source.get("Table1"));
QCOMPARE(m_source.get("Table1")->model(), &m_table1);
QVERIFY(m_source.get("BlueBerryCake"));
QCOMPARE(m_source.get("BlueBerryCake")->model(), &m_table2);
}
void TestTableSource::testRemoval()
{
m_source.remove("Table1");
QCOMPARE(m_source.get("Table1"), (Table*)0);
QVERIFY(m_source.get("Table2"));
QCOMPARE(m_source.get("Table2")->model(), &m_table2);
m_source.remove("Table2");
QCOMPARE(m_source.get("Table1"), (Table*)0);
QCOMPARE(m_source.get("Table2"), (Table*)0);
}
void TestTableSource::testAdding_SAM()
{
QVERIFY(m_source.get("Table3"));
QCOMPARE(m_source.get("Table3")->model(), &m_table3);
QVERIFY(m_source.get("Table4"));
QCOMPARE(m_source.get("Table4")->model(), &m_table4);
}
void TestTableSource::testRenaming_SAM()
{
m_sheetAccessModel.setHeaderData(0, Qt::Horizontal, "RedCarpet");
QVERIFY(m_source.get("RedCarpet"));
QCOMPARE(m_source.get("RedCarpet")->model(), &m_table3);
QVERIFY(m_source.get("Table4"));
QCOMPARE(m_source.get("Table4")->model(), &m_table4);
m_sheetAccessModel.setHeaderData(1, Qt::Horizontal, "Bartholomew");
QVERIFY(m_source.get("RedCarpet"));
QCOMPARE(m_source.get("RedCarpet")->model(), &m_table3);
QVERIFY(m_source.get("Bartholomew"));
QCOMPARE(m_source.get("Bartholomew")->model(), &m_table4);
}
void TestTableSource::testRemoval_SAM()
{
m_sheetAccessModel.removeColumns(0, 1);
QCOMPARE(m_source.get("Table3"), (Table*)0);
QVERIFY(m_source.get("Table4"));
QCOMPARE(m_source.get("Table4")->model(), &m_table4);
m_sheetAccessModel.removeColumns(0, 1);
QCOMPARE(m_source.get("Table3"), (Table*)0);
QCOMPARE(m_source.get("Table4"), (Table*)0);
}
QTEST_MAIN(TestTableSource)
Q_DECLARE_METATYPE(QPointer<QAbstractItemModel>)
diff --git a/plugins/chartshape/tests/odf/TestLoadingBase.cpp b/plugins/chartshape/tests/odf/TestLoadingBase.cpp
index adbe3162e9a..c90fe850a7b 100644
--- a/plugins/chartshape/tests/odf/TestLoadingBase.cpp
+++ b/plugins/chartshape/tests/odf/TestLoadingBase.cpp
@@ -1,212 +1,210 @@
/* This file is part of the KDE project
Copyright 2010 Johannes Simon <johannes.simon@gmail.com>
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
Contact: Suresh Chande suresh.chande@nokia.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoadingBase.h"
// Qt
#include <QTextDocument>
-
-// KDE
-#include <qtest_kde.h>
+#include <QDir>
// Calligra
#include <KoStore.h>
#include <KoOdfReadStore.h>
#include <KoTextShapeDataBase.h>
// KoChart
#include "ChartShape.h"
#include "ChartProxyModel.h"
#include "ChartDocument.h"
#include "PlotArea.h"
#include "Legend.h"
#include "DataSet.h"
#include "TableSource.h"
#include "Axis.h"
// KD Chart
#include <KChartAbstractDiagram>
#include <KChartLegend>
using namespace KoChart;
TestLoadingBase::TestLoadingBase()
: QObject()
{
// No message boxes please.
ChartShape::setEnableUserInteraction(false);
m_chart = new ChartShape(0);
}
void TestLoadingBase::initTestCase()
{
ChartDocument document(m_chart);
QString srcdirname(KDESRCDIR);
QVERIFY(!srcdirname.isEmpty());
QDir srcdir(srcdirname);
QVERIFY(srcdir.exists());
bool hasDocDirInSrcDir = srcdir.cd("doc");
QVERIFY(hasDocDirInSrcDir);
// Go back up, we only used the cd as a test.
if (hasDocDirInSrcDir)
srcdir.cd("..");
KoStore *store = KoStore::createStore(srcdir.absolutePath(), KoStore::Read);
QVERIFY(store->enterDirectory("doc"));
QString errorMsg;
KoOdfReadStore odfReadStore(store);
bool success = odfReadStore.loadAndParse(errorMsg);
if (!success)
qDebug() << "Error in odfReadStore.loadAndParse(): " << errorMsg;
QVERIFY(success);
QVERIFY(document.loadOdf(odfReadStore));
}
void TestLoadingBase::testElementIsVisible(KoShape *element, bool shouldBeVisible)
{
QVERIFY(element);
QCOMPARE(element->isVisible(), shouldBeVisible);
}
void TestLoadingBase::testLegendElements(QStringList labels)
{
QVERIFY(m_chart->legend());
QVERIFY(m_chart->legend()->kdLegend());
QCOMPARE(m_chart->legend()->kdLegend()->datasetCount(),
(unsigned int)labels.count());
QList<KChart::AbstractDiagram*> diagrams = m_chart->legend()->kdLegend()->diagrams();
foreach(KChart::AbstractDiagram *diagram, diagrams) {
QVERIFY(diagram);
QStringList diagramLabels = diagram->datasetLabels();
foreach(const QString &diagramLabel, diagramLabels) {
QVERIFY(!labels.isEmpty());
QCOMPARE(diagramLabel, labels.takeFirst());
}
}
QVERIFY(labels.isEmpty());
}
void TestLoadingBase::testDataSetCellRegions(int dataSetNr,
CellRegion yDataRegion,
CellRegion labelDataRegion,
CellRegion categoryDataRegion,
CellRegion xDataRegion,
CellRegion customDataRegion)
{
QVERIFY(m_chart->proxyModel());
QList<DataSet*> dataSets = m_chart->proxyModel()->dataSets();
QVERIFY(dataSetNr >= 0);
QVERIFY(dataSets.count() > dataSetNr);
DataSet *dataSet = dataSets[dataSetNr];
QVERIFY(dataSet);
int dataSetSize = 0;
dataSetSize = qMax(dataSetSize, yDataRegion.cellCount());
dataSetSize = qMax(dataSetSize, categoryDataRegion.cellCount());
dataSetSize = qMax(dataSetSize, xDataRegion.cellCount());
dataSetSize = qMax(dataSetSize, customDataRegion.cellCount());
QCOMPARE(dataSet->size(), dataSetSize);
QCOMPARE(dataSet->xDataRegion(), xDataRegion);
QCOMPARE(dataSet->yDataRegion(), yDataRegion);
QCOMPARE(dataSet->labelDataRegion(), labelDataRegion);
QCOMPARE(dataSet->categoryDataRegion(), categoryDataRegion);
QCOMPARE(dataSet->customDataRegion(), customDataRegion);
}
void TestLoadingBase::testHasOnlyInternalTable()
{
QVERIFY(m_chart->usesInternalModelOnly());
QVERIFY(internalTable());
}
void TestLoadingBase::testInternalTableSize(int rowCount, int colCount)
{
QAbstractItemModel *model = m_chart->internalModel();
QVERIFY(model);
QVERIFY(m_chart->tableSource()->get(model));
QCOMPARE(model->rowCount(), rowCount);
QCOMPARE(model->columnCount(), colCount);
}
void TestLoadingBase::testTitleText(const QString &text)
{
QVERIFY(m_chart->title());
KoTextShapeDataBase *data = dynamic_cast<KoTextShapeDataBase*>(m_chart->title()->userData());
QVERIFY(data);
QVERIFY(data->document());
QCOMPARE(data->document()->toPlainText(), text);
}
void TestLoadingBase::testSubTitleText(const QString &text)
{
QVERIFY(m_chart->subTitle());
KoTextShapeDataBase *data = dynamic_cast<KoTextShapeDataBase*>(m_chart->subTitle()->userData());
QVERIFY(data);
QVERIFY(data->document());
QCOMPARE(data->document()->toPlainText(), text);
}
void TestLoadingBase::testFooterText(const QString &text)
{
QVERIFY(m_chart->footer());
KoTextShapeDataBase *data = dynamic_cast<KoTextShapeDataBase*>(m_chart->footer()->userData());
QVERIFY(data);
QVERIFY(data->document());
QCOMPARE(data->document()->toPlainText(), text);
}
void TestLoadingBase::testAxisTitle(Axis *axis, const QString &text)
{
QVERIFY(axis);
QVERIFY(axis->title());
KoTextShapeDataBase *data = dynamic_cast<KoTextShapeDataBase*>(axis->title()->userData());
QVERIFY(data);
QVERIFY(data->document());
QCOMPARE(data->document()->toPlainText(), text);
}
Table *TestLoadingBase::internalTable()
{
QAbstractItemModel *internalModel = m_chart->internalModel();
if (!internalModel)
return 0;
return m_chart->tableSource()->get(internalModel);
}
TableSource *TestLoadingBase::tableSource()
{
return m_chart->tableSource();
}
namespace QTest {
template<>
char *toString(const KoChart::CellRegion &region) {
return qstrdup(region.toString().toLatin1());
}
}
diff --git a/plugins/chartshape/tests/odf/TestLoadingBase.h b/plugins/chartshape/tests/odf/TestLoadingBase.h
index e489b6115c1..0bf8c36e640 100644
--- a/plugins/chartshape/tests/odf/TestLoadingBase.h
+++ b/plugins/chartshape/tests/odf/TestLoadingBase.h
@@ -1,109 +1,108 @@
/* This file is part of the KDE project
Copyright 2010 Johannes Simon <johannes.simon@gmail.com>
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
Contact: Suresh Chande suresh.chande@nokia.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KCHART_TESTLOADING_BASE
#define KCHART_TESTLOADING_BASE
// Qt
#include <QObject>
#include <QString>
-
-// KDE
-#include <qtest_kde.h>
+#include <QTest>
// KoChart
#include "CellRegion.h"
class KoShape;
+class QStringList;
namespace KoChart {
class ChartShape;
class Table;
class TableSource;
class Axis;
/**
* Base class for every ODF-loading related unit test.
*
* The philosophy is basically to do as many tests as possible by
* using a helper method from this base class. Lines like these:
* testLegendElements(QStringList() << "Row 1" << "Row 2" << "Row 3");
*
* are much more readable than using flat code by using copy&paste and doing
* the same gets and checks over again in multiple unit tests.
*
*/
class TestLoadingBase : public QObject
{
Q_OBJECT
public:
TestLoadingBase();
protected Q_SLOTS:
virtual void initTestCase();
protected:
// Helper methods to be used by test functions
// 0) Generics
void testElementIsVisible(KoShape *element, bool shouldBeVisible);
// 1) Legend
void testLegendElements(QStringList labels);
// 2) Data Sets
void testDataSetCellRegions(int dataSetNr,
CellRegion yDataRegion,
CellRegion labelDataRegion = CellRegion(),
CellRegion categoryDataRegion = CellRegion(),
CellRegion xDataRegion = CellRegion(),
CellRegion customDataRegion = CellRegion());
// 3) Internal Table
void testHasOnlyInternalTable();
void testInternalTableSize(int rowCount, int colCount);
// 4) Title, Subtitle and Footer
void testTitleText(const QString &text);
void testSubTitleText(const QString &text);
void testFooterText(const QString &text);
// 5) Axes
void testAxisTitle(Axis *axis, const QString &text);
Table* internalTable();
TableSource* tableSource();
ChartShape *m_chart;
};
} // namespace KoChart
namespace QTest {
template<>
char *toString(const KoChart::CellRegion &region);
}
#endif // KCHART_TESTLOADING_BASE
diff --git a/plugins/chartshape/tests/odf/bubble-chart/TestLoading.cpp b/plugins/chartshape/tests/odf/bubble-chart/TestLoading.cpp
index bfc73544cce..9afe877407b 100644
--- a/plugins/chartshape/tests/odf/bubble-chart/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/bubble-chart/TestLoading.cpp
@@ -1,93 +1,89 @@
/* This file is part of the KDE project
@@COPYRIGHT@@
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "ChartShape.h"
#include "PlotArea.h"
#include "Axis.h"
#include "Legend.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::testLabels()
{
testElementIsVisible(m_chart->title(), false);
testElementIsVisible(m_chart->subTitle(), false);
testElementIsVisible(m_chart->footer(), false);
}
void TestLoading::testInternalTable()
{
testHasOnlyInternalTable();
testInternalTableSize(5, 5);
}
void TestLoading::testDataSets()
{
Table *table = internalTable();
QVERIFY(table);
// y data
testDataSetCellRegions(0, CellRegion(table, QRect(2, 2, 1, 4)),
// series label
CellRegion(table, QRect(3, 1, 1, 1)),
// categories (specified in x-axis)
CellRegion(table, QRect(1, 2, 1, 4)),
// x data
CellRegion(),
// bubble widths
CellRegion(table, QRect(3, 2, 1, 4)));
testDataSetCellRegions(1, CellRegion(table, QRect(4, 2, 1, 4)),
CellRegion(table, QRect(5, 1, 1, 1)),
CellRegion(table, QRect(1, 2, 1, 4)),
CellRegion(),
CellRegion(table, QRect(5, 2, 1, 4)));
}
void TestLoading::testPlotArea()
{
testElementIsVisible(m_chart->plotArea(), true);
}
void TestLoading::testLegend()
{
testElementIsVisible(m_chart->legend(), true);
testLegendElements(QStringList() << "Series 1" << "Series 2");
}
void TestLoading::testAxes()
{
testElementIsVisible(m_chart->plotArea()->xAxis()->title(), false);
testElementIsVisible(m_chart->plotArea()->yAxis()->title(), false);
}
-QTEST_KDEMAIN(TestLoading, GUI )
-
+QTEST_MAIN(TestLoading)
diff --git a/plugins/chartshape/tests/odf/bug239802/TestLoading.cpp b/plugins/chartshape/tests/odf/bug239802/TestLoading.cpp
index 5656d9242a5..66630c951b8 100644
--- a/plugins/chartshape/tests/odf/bug239802/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/bug239802/TestLoading.cpp
@@ -1,58 +1,54 @@
/* This file is part of the KDE project
Copyright 2010 Johannes Simon <johannes.simon@gmail.com>
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
Contact: Suresh Chande suresh.chande@nokia.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "ChartShape.h"
#include "PlotArea.h"
#include "Legend.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::testInternalTable()
{
testHasOnlyInternalTable();
testInternalTableSize(5, 4);
}
void TestLoading::testPlotArea()
{
testElementIsVisible(m_chart->plotArea(), true);
}
void TestLoading::testLegend()
{
testElementIsVisible(m_chart->legend(), true);
testLegendElements(QStringList() << "Row 1" << "Row 2" << "Row 3" << "Row 4");
}
-QTEST_KDEMAIN(TestLoading, GUI)
-
+QTEST_MAIN(TestLoading)
diff --git a/plugins/chartshape/tests/odf/default-calligra-chart/TestLoading.cpp b/plugins/chartshape/tests/odf/default-calligra-chart/TestLoading.cpp
index cebc59aad1f..3b06cacb870 100644
--- a/plugins/chartshape/tests/odf/default-calligra-chart/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/default-calligra-chart/TestLoading.cpp
@@ -1,97 +1,93 @@
/* This file is part of the KDE project
@@COPYRIGHT@@
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "ChartShape.h"
#include "PlotArea.h"
#include "Axis.h"
#include "Legend.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::testLabels()
{
testElementIsVisible( m_chart->title(), false );
testElementIsVisible( m_chart->subTitle(), false );
testElementIsVisible( m_chart->footer(), false );
}
void TestLoading::testInternalTable()
{
testHasOnlyInternalTable();
testInternalTableSize( 4, 5 );
}
void TestLoading::testDataSets()
{
Table *table = internalTable();
QVERIFY( table );
// y data
testDataSetCellRegions( 0, CellRegion( table, QRect( 2, 2, 1, 3 ) ),
// series label
CellRegion( table, QRect( 2, 1, 1, 1 ) ),
// categories (specified in x-axis)
CellRegion( table, QRect( 1, 2, 1, 3 ) ) );
testDataSetCellRegions( 1, CellRegion( table, QRect( 3, 2, 1, 3 ) ),
CellRegion( table, QRect( 3, 1, 1, 1 ) ),
CellRegion( table, QRect( 1, 2, 1, 3 ) ) );
testDataSetCellRegions( 2, CellRegion( table, QRect( 4, 2, 1, 3 ) ),
CellRegion( table, QRect( 4, 1, 1, 1 ) ),
CellRegion( table, QRect( 1, 2, 1, 3 ) ) );
testDataSetCellRegions( 3, CellRegion( table, QRect( 5, 2, 1, 3 ) ),
CellRegion( table, QRect( 5, 1, 1, 1 ) ),
CellRegion( table, QRect( 1, 2, 1, 3 ) ) );
}
void TestLoading::testPlotArea()
{
testElementIsVisible( m_chart->plotArea(), true );
}
void TestLoading::testLegend()
{
testElementIsVisible( m_chart->legend(), true );
testLegendElements( QStringList() << "Column 1" << "Column 2" << "Column 3" << "Column 4" );
}
void TestLoading::testAxes()
{
testElementIsVisible( m_chart->plotArea()->xAxis()->title(), true );
testAxisTitle( m_chart->plotArea()->xAxis(), "Month" );
testElementIsVisible( m_chart->plotArea()->yAxis()->title(), true );
testAxisTitle( m_chart->plotArea()->yAxis(), "Growth in %" );
}
-QTEST_KDEMAIN( TestLoading, GUI )
-
+QTEST_MAIN( TestLoading )
diff --git a/plugins/chartshape/tests/odf/default-ooo-chart/TestLoading.cpp b/plugins/chartshape/tests/odf/default-ooo-chart/TestLoading.cpp
index 0c9d95fed02..c54878da2f3 100644
--- a/plugins/chartshape/tests/odf/default-ooo-chart/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/default-ooo-chart/TestLoading.cpp
@@ -1,87 +1,83 @@
/* This file is part of the KDE project
Copyright 2010 Johannes Simon <johannes.simon@gmail.com>
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
Contact: Suresh Chande suresh.chande@nokia.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "TableSource.h"
#include "CellRegion.h"
#include "PlotArea.h"
#include "Legend.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::testLabels()
{
testElementIsVisible(m_chart->title(), false);
testElementIsVisible(m_chart->subTitle(), false);
testElementIsVisible(m_chart->footer(), false);
}
void TestLoading::testInternalTable()
{
testHasOnlyInternalTable();
testInternalTableSize(5, 4);
}
void TestLoading::testDataSets()
{
Table *table = internalTable();
QVERIFY(table);
// y data
testDataSetCellRegions(0, CellRegion(table, QRect(2, 2, 1, 4)),
// series label
CellRegion(table, QRect(2, 1, 1, 1)),
// categories (specified in x-axis)
CellRegion(table, QRect(1, 2, 1, 4)));
testDataSetCellRegions(1, CellRegion(table, QRect(3, 2, 1, 4)),
CellRegion(table, QRect(3, 1, 1, 1)),
CellRegion(table, QRect(1, 2, 1, 4)) );
testDataSetCellRegions(2, CellRegion(table, QRect(4, 2, 1, 4)),
CellRegion(table, QRect(4, 1, 1, 1)),
CellRegion(table, QRect(1, 2, 1, 4)) );
}
void TestLoading::testPlotArea()
{
testElementIsVisible(m_chart->plotArea(), true);
}
void TestLoading::testLegend()
{
testElementIsVisible(m_chart->legend(), true);
testLegendElements(QStringList() << "Spalte 1" << "Spalte 2" << "Spalte 3");
}
-QTEST_KDEMAIN(TestLoading, GUI)
-
+QTEST_MAIN(TestLoading)
diff --git a/plugins/chartshape/tests/odf/me07_percentage_stacked_bar_chart/TestLoading.cpp b/plugins/chartshape/tests/odf/me07_percentage_stacked_bar_chart/TestLoading.cpp
index 1c592187eb3..64ad0df2f3c 100644
--- a/plugins/chartshape/tests/odf/me07_percentage_stacked_bar_chart/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/me07_percentage_stacked_bar_chart/TestLoading.cpp
@@ -1,87 +1,83 @@
/* This file is part of the KDE project
@@COPYRIGHT@@
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
// Qt
#include <QStandardItemModel>
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "ChartShape.h"
#include "TableSource.h"
#include "Legend.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::initTestCase()
{
// Fake sheet data from embedding document
m_sheet.setRowCount(6);
m_sheet.setColumnCount(8);
// Categories
m_sheet.setData(m_sheet.index(3, 4), "Pass");
m_sheet.setData(m_sheet.index(3, 5), "Fail");
m_sheet.setData(m_sheet.index(3, 6), "NA");
// Series label
m_sheet.setData(m_sheet.index(4, 3), "Week");
QVERIFY(tableSource());
tableSource()->add("Sheet1", &m_sheet);
// No actual data needed
// Tell the chart it's embedded
m_chart->setUsesInternalModelOnly(false);
TestLoadingBase::initTestCase();
}
void TestLoading::testInternalTable()
{
QVERIFY(internalTable());
}
void TestLoading::testDataSets()
{
TableSource *source = tableSource();
QVERIFY(source);
// y data
testDataSetCellRegions(0, CellRegion(source, "Sheet1.E5:G5"),
// series label
CellRegion(source, "Sheet1.D5"),
// categories (specified in x-axis)
CellRegion(source, "Sheet1.E4:G4"));
}
void TestLoading::testLegend()
{
testElementIsVisible(m_chart->legend(), true);
testLegendElements(QStringList() << "Week");
}
-QTEST_KDEMAIN(TestLoading, GUI )
-
+QTEST_MAIN(TestLoading)
diff --git a/plugins/chartshape/tests/odf/template/TestLoading.cpp b/plugins/chartshape/tests/odf/template/TestLoading.cpp
index 45e8b6768f7..8e24979673e 100644
--- a/plugins/chartshape/tests/odf/template/TestLoading.cpp
+++ b/plugins/chartshape/tests/odf/template/TestLoading.cpp
@@ -1,58 +1,54 @@
/* This file is part of the KDE project
@@COPYRIGHT@@
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
// Own
#include "TestLoading.h"
-// KDE
-#include <qtest_kde.h>
-
// KoChart
#include "ChartShape.h"
using namespace KoChart;
TestLoading::TestLoading()
: TestLoadingBase()
{
}
void TestLoading::testLabels()
{
// Your code goes here
}
void TestLoading::testInternalTable()
{
// Your code goes here
}
void TestLoading::testDataSets()
{
// Your code goes here
}
void TestLoading::testLegend()
{
// Your code goes here
}
-QTEST_KDEMAIN(TestLoading, GUI)
-
+QTEST_MAIN(TestLoading)
diff --git a/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp b/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp
index b687e217284..c55a5aa8387 100644
--- a/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp
+++ b/plugins/colorengines/lcms2/IccColorSpaceEngine.cpp
@@ -1,224 +1,224 @@
/*
* Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "IccColorSpaceEngine.h"
#include "KoColorModelStandardIds.h"
#include <klocale.h>
#include "LcmsColorSpace.h"
-#include "QDebug"
+#include <kdebug.h>
// -- KoLcmsColorConversionTransformation --
class KoLcmsColorConversionTransformation : public KoColorConversionTransformation
{
public:
KoLcmsColorConversionTransformation(const KoColorSpace* srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer* srcProfile,
const KoColorSpace* dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer* dstProfile,
Intent renderingIntent,
ConversionFlags conversionFlags)
: KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags)
, m_transform(0)
{
Q_ASSERT(srcCs);
Q_ASSERT(dstCs);
Q_ASSERT(renderingIntent < 4);
if (srcCs->colorDepthId() == Integer8BitsColorDepthID
|| srcCs->colorDepthId() == Integer16BitsColorDepthID) {
if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) ||
dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) &&
!conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization) ) {
conversionFlags |= KoColorConversionTransformation::NoOptimization;
}
}
m_transform = cmsCreateTransform(srcProfile->lcmsProfile(),
srcColorSpaceType,
dstProfile->lcmsProfile(),
dstColorSpaceType,
renderingIntent,
conversionFlags);
Q_ASSERT(m_transform);
}
~KoLcmsColorConversionTransformation() {
cmsDeleteTransform(m_transform);
}
public:
virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const
{
Q_ASSERT(m_transform);
qint32 srcPixelSize = srcColorSpace()->pixelSize();
qint32 dstPixelSize = dstColorSpace()->pixelSize();
cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
// Lcms does nothing to the destination alpha channel so we must convert that manually.
while (numPixels > 0) {
qreal alpha = srcColorSpace()->opacityF(src);
dstColorSpace()->setOpacity(dst, alpha, 1);
src += srcPixelSize;
dst += dstPixelSize;
numPixels--;
}
}
private:
mutable cmsHTRANSFORM m_transform;
};
struct IccColorSpaceEngine::Private {
};
IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private)
{
}
IccColorSpaceEngine::~IccColorSpaceEngine()
{
delete d;
}
void IccColorSpaceEngine::addProfile(const QString &filename)
{
KoColorSpaceRegistry* registry = KoColorSpaceRegistry::instance();
KoColorProfile *profile = new IccColorProfile(filename);
Q_CHECK_PTR(profile);
// this our own loading code; sometimes it fails because of an lcms error
profile->load();
// and then lcms can read the profile from file itself without problems,
// quite often, and we can initialize it
if (!profile->valid()) {
cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r");
profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp);
}
if (profile->valid()) {
kDebug(31000) << "Valid profile : " << profile->fileName() << profile->name();
registry->addProfile(profile);
} else {
kDebug(31000) << "Invalid profile : " << profile->fileName() << profile->name();
delete profile;
}
}
void IccColorSpaceEngine::removeProfile(const QString &filename)
{
KoColorSpaceRegistry* registry = KoColorSpaceRegistry::instance();
KoColorProfile *profile = new IccColorProfile(filename);
Q_CHECK_PTR(profile);
profile->load();
if (profile->valid() && registry->profileByName(profile->name())) {
registry->removeProfile(profile);
}
}
KoColorConversionTransformation* IccColorSpaceEngine::createColorTransformation(const KoColorSpace* srcColorSpace,
const KoColorSpace* dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
Q_ASSERT(srcColorSpace);
Q_ASSERT(dstColorSpace);
return new KoLcmsColorConversionTransformation(
srcColorSpace, computeColorSpaceType(srcColorSpace),
dynamic_cast<const IccColorProfile*>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
dynamic_cast<const IccColorProfile*>(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags);
}
quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace* cs) const
{
Q_ASSERT(cs);
if (const KoLcmsInfo *lcmsInfo = dynamic_cast<const KoLcmsInfo*>(cs)) {
return lcmsInfo->colorSpaceType();
}
else {
QString modelId = cs->colorModelId().id();
QString depthId = cs->colorDepthId().id();
// Compute the depth part of the type
quint32 depthType;
if (depthId == Integer8BitsColorDepthID.id()) {
depthType = BYTES_SH(1);
}
else if (depthId == Integer16BitsColorDepthID.id()) {
depthType = BYTES_SH(2);
}
else if (depthId == Float16BitsColorDepthID.id()) {
depthType = BYTES_SH(2);
}
else if (depthId == Float32BitsColorDepthID.id()) {
depthType = BYTES_SH(4);
}
else if (depthId == Float64BitsColorDepthID.id()) {
depthType = BYTES_SH(0);
}
else {
qWarning() << "Unknow bit depth";
return 0;
}
// Compute the model part of the type
quint32 modelType = 0;
if (modelId == RGBAColorModelID.id()) {
if (depthId.startsWith(QLatin1Char('U'))) {
modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1));
}
else if (depthId.startsWith(QLatin1Char('F'))) {
modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) );
}
} else if (modelId == XYZAColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3));
} else if (modelId == LABAColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3));
} else if (modelId == CMYKAColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4));
} else if (modelId == GrayAColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1));
} else if (modelId == GrayColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1));
} else if (modelId == YCbCrAColorModelID.id()) {
modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3));
} else {
qWarning() << "Cannot convert colorspace to lcms modeltype";
return 0;
}
return depthType | modelType;
}
}
diff --git a/plugins/colorengines/lcms2/LcmsEnginePlugin.cpp b/plugins/colorengines/lcms2/LcmsEnginePlugin.cpp
index 727caec0a7f..095c530db48 100644
--- a/plugins/colorengines/lcms2/LcmsEnginePlugin.cpp
+++ b/plugins/colorengines/lcms2/LcmsEnginePlugin.cpp
@@ -1,289 +1,290 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004,2010 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "LcmsEnginePlugin.h"
#include <QHash>
#include <QStringList>
#include <QDir>
#include <kcomponentdata.h>
#include <kpluginfactory.h>
#include <kstandarddirs.h>
#include <klocale.h>
#include <kglobal.h>
+#include <kdebug.h>
#include <KoBasicHistogramProducers.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceEngine.h>
#include "IccColorSpaceEngine.h"
#include "colorprofiles/LcmsColorProfileContainer.h"
#include "colorspaces/cmyk_u8/CmykU8ColorSpace.h"
#include "colorspaces/cmyk_u16/CmykU16ColorSpace.h"
#include "colorspaces/cmyk_f32/CmykF32ColorSpace.h"
#include "colorspaces/gray_u8/GrayU8ColorSpace.h"
#include "colorspaces/gray_u16/GrayU16ColorSpace.h"
#include "colorspaces/gray_f32/GrayF32ColorSpace.h"
#include "colorspaces/lab_u8/LabU8ColorSpace.h"
#include "colorspaces/lab_u16/LabColorSpace.h"
#include "colorspaces/lab_f32/LabF32ColorSpace.h"
#include "colorspaces/xyz_u8/XyzU8ColorSpace.h"
#include "colorspaces/xyz_u16/XyzU16ColorSpace.h"
#include "colorspaces/xyz_f32/XyzF32ColorSpace.h"
#include "colorspaces/rgb_u8/RgbU8ColorSpace.h"
#include "colorspaces/rgb_u16/RgbU16ColorSpace.h"
#include "colorspaces/rgb_f32/RgbF32ColorSpace.h"
#include "colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h"
#include "colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h"
#include "colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h"
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#ifdef HAVE_LCMS24
#include "colorspaces/xyz_f16/XyzF16ColorSpace.h"
#include "colorspaces/rgb_f16/RgbF16ColorSpace.h"
#endif
#endif
void lcms2LogErrorHandlerFunction(cmsContext /*ContextID*/, cmsUInt32Number ErrorCode, const char *Text)
{
kError(31000) << "Lcms2 error: " << ErrorCode << Text;
}
K_PLUGIN_FACTORY_WITH_JSON(PluginFactory, "kolcmsengine.json",
registerPlugin<LcmsEnginePlugin>();)
LcmsEnginePlugin::LcmsEnginePlugin(QObject *parent, const QVariantList &)
: QObject(parent)
{
kDebug(31000) << "Initializing the lcms engine plugin";
// Set the lmcs error reporting function
cmsSetLogErrorHandler(&lcms2LogErrorHandlerFunction);
KoColorSpaceRegistry* registry = KoColorSpaceRegistry::instance();
// Initialise color engine
KoColorSpaceEngineRegistry::instance()->add(new IccColorSpaceEngine);
// prepare a list of the ICC profiles
KGlobal::dirs()->addResourceType("icc_profiles", "data", "color/icc");
QStringList profileFilenames;
profileFilenames += KGlobal::dirs()->findAllResources("icc_profiles", "*.icm", KStandardDirs::Recursive);
profileFilenames += KGlobal::dirs()->findAllResources("icc_profiles", "*.ICM", KStandardDirs::Recursive);
profileFilenames += KGlobal::dirs()->findAllResources("icc_profiles", "*.ICC", KStandardDirs::Recursive);
profileFilenames += KGlobal::dirs()->findAllResources("icc_profiles", "*.icc", KStandardDirs::Recursive);
// Load the profiles
if (!profileFilenames.empty()) {
KoColorProfile * profile = 0;
for (QStringList::Iterator it = profileFilenames.begin(); it != profileFilenames.end(); ++it) {
profile = new IccColorProfile(*it);
Q_CHECK_PTR(profile);
profile->load();
if (profile->valid()) {
kDebug(31000) << "Valid profile : " << profile->fileName() << profile->name();
registry->addProfileToMap(profile);
} else {
kDebug(31000) << "Invalid profile : " << profile->fileName() << profile->name();
delete profile;
}
}
}
// ------------------- LAB ---------------------------------
KoColorProfile *labProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateLab2Profile(NULL));
registry->addProfile(labProfile);
registry->add(new LabU8ColorSpaceFactory());
registry->add(new LabU16ColorSpaceFactory());
registry->add(new LabF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("LABAU8HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("LABAU16HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer16BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("LABAF32HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Float32BitsColorDepthID.id()));
// ------------------- RGB ---------------------------------
KoColorProfile *rgbProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreate_sRGBProfile());
registry->addProfile(rgbProfile);
registry->add(new RgbU8ColorSpaceFactory());
registry->add(new RgbU16ColorSpaceFactory());
#ifdef HAVE_LCMS24
#ifdef HAVE_OPENEXR
registry->add(new RgbF16ColorSpaceFactory());
#endif
#endif
registry->add(new RgbF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("RGBU8HISTO", i18n("RGB8 Histogram")), RGBAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("RGBU16HISTO", i18n("RGB16 Histogram")), RGBAColorModelID.id(), Integer16BitsColorDepthID.id()));
#ifdef HAVE_LCMS24
#ifdef HAVE_OPENEXR
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF16HalfHistogramProducer>
(KoID("RGBF16HISTO", i18n("RGBF16 Histogram")), RGBAColorModelID.id(), Float16BitsColorDepthID.id()));
#endif
#endif
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("RGF328HISTO", i18n("RGBF32 Histogram")), RGBAColorModelID.id(), Float32BitsColorDepthID.id()));
// ------------------- GRAY ---------------------------------
cmsToneCurve* Gamma = cmsBuildGamma(0, 2.2);
cmsHPROFILE hProfile = cmsCreateGrayProfile(cmsD50_xyY(), Gamma);
cmsFreeToneCurve(Gamma);
KoColorProfile *defProfile = LcmsColorProfileContainer::createFromLcmsProfile(hProfile);
registry->addProfile(defProfile);
registry->add(new GrayAU8ColorSpaceFactory());
registry->add(new GrayAU16ColorSpaceFactory());
registry->add(new GrayF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("GRAYA8HISTO", i18n("GRAY/Alpha8 Histogram")), GrayAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("GRAYA16HISTO", i18n("GRAY/Alpha16 Histogram")), GrayAColorModelID.id(), Integer16BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("GRAYAF32HISTO", i18n("GRAY/Alpha 32 float Histogram")), GrayAColorModelID.id(), Float32BitsColorDepthID.id()));
// ------------------- CMYK ---------------------------------
registry->add(new CmykU8ColorSpaceFactory());
registry->add(new CmykU16ColorSpaceFactory());
registry->add(new CmykF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("CMYK8HISTO", i18n("CMYK8 Histogram")), CMYKAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("CMYK16HISTO", i18n("CMYK16 Histogram")), CMYKAColorModelID.id(), Integer16BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("CMYKF32HISTO", i18n("CMYK F32 Histogram")), CMYKAColorModelID.id(), Float32BitsColorDepthID.id()));
// ------------------- XYZ ---------------------------------
KoColorProfile *xyzProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateXYZProfile());
registry->addProfile(xyzProfile);
registry->add(new XyzU8ColorSpaceFactory());
registry->add(new XyzU16ColorSpaceFactory());
#ifdef HAVE_LCMS24
#ifdef HAVE_OPENEXR
registry->add(new XyzF16ColorSpaceFactory());
#endif
#endif
registry->add(new XyzF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("XYZ8HISTO", i18n("XYZ8 Histogram")), XYZAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("XYZ16HISTO", i18n("XYZ16 Histogram")), XYZAColorModelID.id(), Integer16BitsColorDepthID.id()));
#ifdef HAVE_LCMS24
#ifdef HAVE_OPENEXR
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("XYZF16HISTO", i18n("XYZF16 Histogram")), XYZAColorModelID.id(), Float16BitsColorDepthID.id()));
#endif
#endif
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("XYZF32HISTO", i18n("XYZF32 Histogram")), XYZAColorModelID.id(), Float32BitsColorDepthID.id()));
// ------------------- YCBCR ---------------------------------
// KoColorProfile *yCbCrProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateYCBCRProfile());
// registry->addProfile(yCbCrProfile);
registry->add(new YCbCrU8ColorSpaceFactory());
registry->add(new YCbCrU16ColorSpaceFactory());
registry->add(new YCbCrF32ColorSpaceFactory());
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU8HistogramProducer>
(KoID("YCBCR8HISTO", i18n("YCBCR8 Histogram")), YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicU16HistogramProducer>
(KoID("YCBCR16HISTO", i18n("YCBCR16 Histogram")), YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id()));
KoHistogramProducerFactoryRegistry::instance()->add(
new KoBasicHistogramProducerFactory<KoBasicF32HistogramProducer>
(KoID("YCBCRF32HISTO", i18n("YCBCRF32 Histogram")), YCbCrAColorModelID.id(), Float32BitsColorDepthID.id()));
// Add profile alias for default profile from lcms1
registry->addProfileAlias("sRGB built-in - (lcms internal)", "sRGB built-in");
registry->addProfileAlias("gray built-in - (lcms internal)", "gray built-in");
registry->addProfileAlias("Lab identity built-in - (lcms internal)", "Lab identity built-in");
registry->addProfileAlias("XYZ built-in - (lcms internal)", "XYZ identity built-in");
}
#include <LcmsEnginePlugin.moc>
diff --git a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp
index 612da3100fa..e6d330af791 100644
--- a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp
+++ b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.cpp
@@ -1,97 +1,95 @@
#include "TestKoColorSpaceRegistry.h"
-#include <qtest_kde.h>
+#include <QTest>
#include "KoColorSpaceRegistry.h"
#include "KoColorSpace.h"
#include "RgbU8ColorSpace.h"
#include "RgbU16ColorSpace.h"
#include "LabColorSpace.h"
void TestKoColorSpaceRegistry::testConstruction()
{
KoColorSpaceRegistry* instance = KoColorSpaceRegistry::instance();
Q_ASSERT(instance);
}
void TestKoColorSpaceRegistry::testRgbU8()
{
QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(RGBAColorModelID,
Integer8BitsColorDepthID);
const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId);
QVERIFY(colorSpaceFactory != 0);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
QVERIFY(colorSpace != 0);
const KoColorProfile *profile = colorSpace->profile();
QVERIFY(profile != 0);
QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile());
cmsHPROFILE lcmsProfile = cmsCreate_sRGBProfile();
QString testProfileName = "TestRGBU8ProfileName";
cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, "");
}
void TestKoColorSpaceRegistry::testRgbU16()
{
QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(RGBAColorModelID,
Integer16BitsColorDepthID);
const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId);
QVERIFY(colorSpaceFactory != 0);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb16();
QVERIFY(colorSpace != 0);
const KoColorProfile *profile = colorSpace->profile();
QVERIFY(profile != 0);
QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile());
cmsHPROFILE lcmsProfile = cmsCreate_sRGBProfile();
QString testProfileName = "TestRGBU16ProfileName";
cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, "");
}
void TestKoColorSpaceRegistry::testLab()
{
QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(LABAColorModelID,
Integer16BitsColorDepthID);
const KoColorSpaceFactory *colorSpaceFactory = KoColorSpaceRegistry::instance()->colorSpaceFactory(colorSpaceId);
QVERIFY(colorSpaceFactory != 0);
const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->lab16();
QVERIFY(colorSpace != 0);
const KoColorProfile *profile = colorSpace->profile();
QVERIFY(profile != 0);
QCOMPARE(profile->name(), colorSpaceFactory->defaultProfile());
cmsCIExyY whitepoint;
whitepoint.x = 0.33;
whitepoint.y = 0.33;
whitepoint.Y = 1.0;
cmsHPROFILE lcmsProfile = cmsCreateLab2Profile(&whitepoint);
QString testProfileName = "TestLabProfileName";
cmsWriteTag(lcmsProfile, cmsSigProfileDescriptionTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceModelDescTag, testProfileName.toLatin1().constData());
cmsWriteTag(lcmsProfile, cmsSigDeviceMfgDescTag, "");
}
-QTEST_KDEMAIN(TestKoColorSpaceRegistry, NoGUI)
-#include <TestKoColorSpaceRegistry.moc>
-
+QTEST_GUILESS_MAIN(TestKoColorSpaceRegistry)
diff --git a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.h b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.h
index afdecfc9f59..18badc9daae 100644
--- a/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.h
+++ b/plugins/colorengines/lcms2/tests/TestKoColorSpaceRegistry.h
@@ -1,16 +1,16 @@
#ifndef TESTKOCOLORSPACEREGISTRY_H
#define TESTKOCOLORSPACEREGISTRY_H
-#include <QtTest>
+#include <QObject>
class TestKoColorSpaceRegistry : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testConstruction();
void testRgbU8();
void testRgbU16();
void testLab();
};
#endif
diff --git a/plugins/colorengines/lcms2/tests/TestKoCompositeOps.cpp b/plugins/colorengines/lcms2/tests/TestKoCompositeOps.cpp
index 497f95e91e0..548695e2080 100644
--- a/plugins/colorengines/lcms2/tests/TestKoCompositeOps.cpp
+++ b/plugins/colorengines/lcms2/tests/TestKoCompositeOps.cpp
@@ -1,1301 +1,1299 @@
/*
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestKoCompositeOps.h"
-#include <qtest_kde.h>
+#include <QTest>
#include <KoColorSpace.h>
#include "../compositeops/KoCompositeOpAlphaDarken.h"
#include "../compositeops/KoCompositeOpOver.h"
#include <KoColorSpaceTraits.h>
#define FULL_OPACITY KoColorSpaceMathsTraits<quint16>::unitValue
#define HALF_OPACITY (FULL_OPACITY/2)
#define QUARTER_OPACITY (FULL_OPACITY/4)
#define QCOMPAREui(a,b) QCOMPARE(a, (quint16)b)
#include <KoCompositeOpDivide.h>
#include <KoCompositeOpDodge.h>
#include <KoCompositeOpInversedSubtract.h>
#include <KoCompositeOpMultiply.h>
#include <KoCompositeOpOverlay.h>
#include <KoCompositeOpScreen.h>
#include <KoCompositeOpSubtract.h>
#include <KoCompositeOpCopy.h>
#include <KoCompositeOpCopy2.h>
#include <KoColorSpaceRegistry.h>
#include <KoColor.h>
void TestKoCompositeOps::testCompositeOver()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpOver<KoBgrU16Traits> over(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 13760);
QCOMPAREui(p16f1.green, 4472);
QCOMPAREui(p16f1.blue, 16992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 12501);
QCOMPAREui(p16f1.green, 7999);
QCOMPAREui(p16f1.blue, 17999);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src, dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 11667);
QCOMPAREui(p16f1.green, 10333);
QCOMPAREui(p16f1.blue, 18666);
QCOMPAREui(p16f1.alpha, 49151);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 13001);
QCOMPAREui(p16f1.green, 6599);
QCOMPAREui(p16f1.blue, 17599);
QCOMPAREui(p16f1.alpha, 40959);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
over.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 11000);
QCOMPAREui(p16f1.green, 12200);
QCOMPAREui(p16f1.blue, 19200);
QCOMPAREui(p16f1.alpha, 40959);
}
void TestKoCompositeOps::testCompositeAlphaDarken()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpAlphaDarken<KoBgrU16Traits> alphaDarken(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 13760);
QCOMPAREui(p16f1.green, 4472);
QCOMPAREui(p16f1.blue, 16992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 12501);
QCOMPAREui(p16f1.green, 7999);
QCOMPAREui(p16f1.blue, 17999);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 12501);
QCOMPAREui(p16f1.green, 7999);
QCOMPAREui(p16f1.blue, 17999);
QCOMPAREui(p16f1.alpha, 49150);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 13751);
QCOMPAREui(p16f1.green, 4499);
QCOMPAREui(p16f1.blue, 16999);
QCOMPAREui(p16f1.alpha, 40958);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
alphaDarken.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 12501);
QCOMPAREui(p16f1.green, 7999);
QCOMPAREui(p16f1.blue, 17999);
QCOMPAREui(p16f1.alpha, 40958);
}
void TestKoCompositeOps::testCompositeDivide()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpDivide<KoBgrU16Traits> divide(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 65535);
QCOMPAREui(p16f1.green, 4369);
QCOMPAREui(p16f1.blue, 52426);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 40168);
QCOMPAREui(p16f1.green, 2677);
QCOMPAREui(p16f1.blue, 34141);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 40168);
QCOMPAREui(p16f1.green, 2677);
QCOMPAREui(p16f1.blue, 34141);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 27534);
QCOMPAREui(p16f1.green, 1835);
QCOMPAREui(p16f1.blue, 25034);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 48690);
QCOMPAREui(p16f1.green, 3246);
QCOMPAREui(p16f1.blue, 40284);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 40267);
QCOMPAREui(p16f1.green, 2684);
QCOMPAREui(p16f1.blue, 34212);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 48690);
QCOMPAREui(p16f1.green, 3246);
QCOMPAREui(p16f1.blue, 40284);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 35213);
QCOMPAREui(p16f1.green, 2347);
QCOMPAREui(p16f1.blue, 30569);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
divide.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 43877);
QCOMPAREui(p16f1.green, 2925);
QCOMPAREui(p16f1.blue, 36815);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeDodge()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpDodge<KoBgrU16Traits> dodge(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 17700);
QCOMPAREui(p16f1.green, 1296);
QCOMPAREui(p16f1.blue, 23027);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 16344);
QCOMPAREui(p16f1.green, 1147);
QCOMPAREui(p16f1.blue, 19499);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 16344);
QCOMPAREui(p16f1.green, 1147);
QCOMPAREui(p16f1.blue, 19499);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 15669);
QCOMPAREui(p16f1.green, 1073);
QCOMPAREui(p16f1.blue, 17742);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 16800);
QCOMPAREui(p16f1.green, 1197);
QCOMPAREui(p16f1.blue, 20684);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 16349);
QCOMPAREui(p16f1.green, 1147);
QCOMPAREui(p16f1.blue, 19513);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 16800);
QCOMPAREui(p16f1.green, 1197);
QCOMPAREui(p16f1.blue, 20684);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 16079);
QCOMPAREui(p16f1.green, 1118);
QCOMPAREui(p16f1.blue, 18810);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
dodge.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 16542);
QCOMPAREui(p16f1.green, 1169);
QCOMPAREui(p16f1.blue, 20015);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeInversedSubtract()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpInversedSubtract<KoBgrU16Traits> inversedSubtract(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 0);
QCOMPAREui(p16f1.green, 14000);
QCOMPAREui(p16f1.blue, 4000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 7530);
QCOMPAREui(p16f1.green, 7474);
QCOMPAREui(p16f1.blue, 10024);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 7530);
QCOMPAREui(p16f1.green, 7474);
QCOMPAREui(p16f1.blue, 10024);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 11280);
QCOMPAREui(p16f1.green, 4224);
QCOMPAREui(p16f1.blue, 13024);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 5000);
QCOMPAREui(p16f1.green, 9666);
QCOMPAREui(p16f1.blue, 8000);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 7501);
QCOMPAREui(p16f1.green, 7499);
QCOMPAREui(p16f1.blue, 10001);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 5000);
QCOMPAREui(p16f1.green, 9666);
QCOMPAREui(p16f1.blue, 8000);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 9001);
QCOMPAREui(p16f1.green, 6199);
QCOMPAREui(p16f1.blue, 11201);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
inversedSubtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 6429);
QCOMPAREui(p16f1.green, 8428);
QCOMPAREui(p16f1.blue, 9143);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeMulitply()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpMultiply<KoBgrU16Traits> mulitply(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 2289);
QCOMPAREui(p16f1.green, 229);
QCOMPAREui(p16f1.blue, 4883);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 8670);
QCOMPAREui(p16f1.green, 617);
QCOMPAREui(p16f1.blue, 10464);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 8670);
QCOMPAREui(p16f1.green, 617);
QCOMPAREui(p16f1.blue, 10464);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 11848);
QCOMPAREui(p16f1.green, 809);
QCOMPAREui(p16f1.blue, 13243);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 6526);
QCOMPAREui(p16f1.green, 486);
QCOMPAREui(p16f1.blue, 8589);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 8645);
QCOMPAREui(p16f1.green, 615);
QCOMPAREui(p16f1.blue, 10442);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 6526);
QCOMPAREui(p16f1.green, 486);
QCOMPAREui(p16f1.blue, 8589);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 9916);
QCOMPAREui(p16f1.green, 692);
QCOMPAREui(p16f1.blue, 11554);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
mulitply.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 7737);
QCOMPAREui(p16f1.green, 560);
QCOMPAREui(p16f1.blue, 9648);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeOverlay()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpOverlay<KoBgrU16Traits> overlay(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 6963);
QCOMPAREui(p16f1.green, 466);
QCOMPAREui(p16f1.blue, 11288);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 10998);
QCOMPAREui(p16f1.green, 735);
QCOMPAREui(p16f1.blue, 13654);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 10998);
QCOMPAREui(p16f1.green, 735);
QCOMPAREui(p16f1.blue, 13654);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 13007);
QCOMPAREui(p16f1.green, 868);
QCOMPAREui(p16f1.blue, 14832);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 9642);
QCOMPAREui(p16f1.green, 644);
QCOMPAREui(p16f1.blue, 12859);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10982);
QCOMPAREui(p16f1.green, 734);
QCOMPAREui(p16f1.blue, 13645);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 9642);
QCOMPAREui(p16f1.green, 644);
QCOMPAREui(p16f1.blue, 12859);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 11786);
QCOMPAREui(p16f1.green, 787);
QCOMPAREui(p16f1.blue, 14116);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
overlay.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10408);
QCOMPAREui(p16f1.green, 695);
QCOMPAREui(p16f1.blue, 13308);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeScreen()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpScreen<KoBgrU16Traits> screen(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 22711);
QCOMPAREui(p16f1.green, 15771);
QCOMPAREui(p16f1.blue, 31117);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 18840);
QCOMPAREui(p16f1.green, 8356);
QCOMPAREui(p16f1.blue, 23528);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 18840);
QCOMPAREui(p16f1.green, 8356);
QCOMPAREui(p16f1.blue, 23528);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 16912);
QCOMPAREui(p16f1.green, 4663);
QCOMPAREui(p16f1.blue, 19749);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 20140);
QCOMPAREui(p16f1.green, 10847);
QCOMPAREui(p16f1.blue, 26078);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 18855);
QCOMPAREui(p16f1.green, 8385);
QCOMPAREui(p16f1.blue, 23558);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 20140);
QCOMPAREui(p16f1.green, 10847);
QCOMPAREui(p16f1.blue, 26078);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 18084);
QCOMPAREui(p16f1.green, 6908);
QCOMPAREui(p16f1.blue, 22046);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
screen.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 19406);
QCOMPAREui(p16f1.green, 9440);
QCOMPAREui(p16f1.blue, 24638);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeSubtract()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpSubtract<KoBgrU16Traits> subtract(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 5000);
QCOMPAREui(p16f1.green, 0);
QCOMPAREui(p16f1.blue, 0);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 10020);
QCOMPAREui(p16f1.green, 502);
QCOMPAREui(p16f1.blue, 8032);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 10020);
QCOMPAREui(p16f1.green, 502);
QCOMPAREui(p16f1.blue, 8032);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 12520);
QCOMPAREui(p16f1.green, 752);
QCOMPAREui(p16f1.blue, 12032);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 15000);
QCOMPAREui(p16f1.green, 1000);
QCOMPAREui(p16f1.blue, 16000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 8334);
QCOMPAREui(p16f1.green, 334);
QCOMPAREui(p16f1.blue, 5334);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10001);
QCOMPAREui(p16f1.green, 501);
QCOMPAREui(p16f1.blue, 8001);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 8334);
QCOMPAREui(p16f1.green, 334);
QCOMPAREui(p16f1.blue, 5334);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 11001);
QCOMPAREui(p16f1.green, 601);
QCOMPAREui(p16f1.blue, 9601);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
subtract.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 9286);
QCOMPAREui(p16f1.green, 429);
QCOMPAREui(p16f1.blue, 6858);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
}
void TestKoCompositeOps::testCompositeCopy2()
{
KoBgrU16Traits::Pixel p16f;
KoBgrU16Traits::Pixel p16f1;
quint8* p16fPtr = reinterpret_cast<quint8*>(&p16f);
quint8* p16fPtr1 = reinterpret_cast<quint8*>(&p16f1);
KoCompositeOpCopy2<KoBgrU16Traits> copy(0);
// Test no mask, full opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, full opacity
quint8 mask; mask = 127;
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 255);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test mask, half opacity
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 1, 1, 1, 127);
QCOMPAREui(p16f1.red, 13760);
QCOMPAREui(p16f1.green, 4472);
QCOMPAREui(p16f1.blue, 16992);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, transparent source
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = 0;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, 0);
// Test no mask, full opacity, transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = 0;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = FULL_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, FULL_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = FULL_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, full opacity, quarter-transparent src, half-transparent dst
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = QUARTER_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = HALF_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, QUARTER_OPACITY);
// Test no mask, full opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 255);
QCOMPAREui(p16f1.red, 10000);
QCOMPAREui(p16f1.green, 15000);
QCOMPAREui(p16f1.blue, 20000);
QCOMPAREui(p16f1.alpha, HALF_OPACITY);
// Test no mask, half opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, 0, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 12510);
QCOMPAREui(p16f1.green, 7972);
QCOMPAREui(p16f1.blue, 17992);
QCOMPAREui(p16f1.alpha, 24542);
// Test mask, half opacity, quarter-transparent dst, half-transparent src
p16f.red = 10000; p16f.green = 15000; p16f.blue = 20000; p16f.alpha = HALF_OPACITY;
p16f1.red = 15000; p16f1.green = 1000; p16f1.blue = 16000; p16f1.alpha = QUARTER_OPACITY;
copy.composite(p16fPtr1, KoBgrU16Traits::pixelSize, p16fPtr, KoBgrU16Traits::pixelSize, &mask, 0, 1, 1, 127);
QCOMPAREui(p16f1.red, 13760);
QCOMPAREui(p16f1.green, 4472);
QCOMPAREui(p16f1.blue, 16992);
QCOMPAREui(p16f1.alpha, 20447);
}
void TestKoCompositeOps::testCompositeCopy()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp * copy = cs->compositeOp(COMPOSITE_COPY);
KoColor black(Qt::black, cs);
KoColor white(Qt::white, cs);
KoColor opaque(QColor(0,0,0,0), cs);
int w = 512;
int h = 512;
int pixelCount = w * h;
quint32 pixelSize = cs->pixelSize();
// dst
quint8 * layer = new quint8[pixelCount * pixelSize];
quint8 * iter = layer;
for (int i = 0; i < pixelCount; i++){
memcpy(iter, white.data() , pixelSize);
iter += pixelSize;
}
// full white image
//cs->convertToQImage(layer, w, h, 0,KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags).save("0dst.png");
// src
quint8 * dab = new quint8[pixelCount * pixelSize];
iter = dab;
for (int i = 0; i < pixelCount; i++){
memcpy(iter, black.data() , pixelSize);
iter += pixelSize;
}
// full black image
//cs->convertToQImage(dab, w, h, 0,KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags).save("1src.png");
// selection
quint32 selectionPixelSize = KoColorSpaceRegistry::instance()->alpha8()->pixelSize();
quint8 * selection = new quint8[pixelCount * selectionPixelSize];
iter = selection;
for (int height = 0; height < h; height++){
for (int width = 0; width < w; width++){
if ((height > 128) && (height < 256) && (width > 128) && (width < 256)){
*iter = 255;
}else{
*iter = 0;
}
iter += selectionPixelSize;
}
}
// white rectangle at 128,128
//KoColorSpaceRegistry::instance()->alpha8()->convertToQImage(selection, w, h, 0, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags).save("1mask.png");
copy->composite(layer,w * pixelSize,
dab, w * pixelSize,
0,0,
h, w,
255,
QBitArray());
// full black image
//cs->convertToQImage(layer, w, h, 0,KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags).save("2result.png");
copy->composite(layer,w * pixelSize,
opaque.data(), 0,
0,0,
h,w,
255,
QBitArray()
);
// full opaque image
//cs->convertToQImage(layer, w, h, 0,KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags).save("3result.png");
copy->composite(layer,w * pixelSize,
dab, w * pixelSize,
selection, w * selectionPixelSize,
h,w,
255,
QBitArray()
);
// black rectangle on opaque background
QImage result = cs->convertToQImage(layer, w, h, 0, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
QImage expectedResult(QString(FILES_DATA_DIR) + QDir::separator() + "CopyWithSelectionExpectedResult.png");
bool testOk = (result == expectedResult);
if (!testOk){
qDebug() << "Saving the result";
result.save("CopyWithSelection.png");
}
QVERIFY2(testOk, "Images are not equal");
copy->composite(layer, w * pixelSize,
white.data(), 0,
selection, w * selectionPixelSize,
h,w,
255,
QBitArray());
result = cs->convertToQImage(layer, w, h, 0, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
expectedResult = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "CopySingleWithSelectionExpectedResult.png");
testOk = (result == expectedResult);
if (!testOk){
qDebug() << expectedResult.size() << result.size();
for (int row = 0; row < expectedResult.size().height(); ++row) {
for (int col = 0; col < expectedResult.size().width(); ++ col) {
QRgb res = result.pixel(col, row);
QRgb exp = expectedResult.pixel(col, row);
if (res != exp) {
qDebug() << "wrong pixel:" << col << "," << row
<< "result:" << qRed(res) << qGreen(res) << qBlue(res) << qAlpha(res)
<< "expected" << qRed(exp) << qGreen(exp) << qBlue(exp) << qAlpha(exp);
}
}
}
expectedResult.save("expected result.png");
result.save("CopySingleWithSelection.png");
QFAIL("Images with single pixel and selection are not equal");
}
}
-
-QTEST_KDEMAIN(TestKoCompositeOps, NoGUI)
-#include "TestKoCompositeOps.moc"
+QTEST_GUILESS_MAIN(TestKoCompositeOps)
diff --git a/plugins/colorengines/lcms2/tests/TestKoCompositeOps.h b/plugins/colorengines/lcms2/tests/TestKoCompositeOps.h
index 15a7dcea1f5..667fcc2b2d6 100644
--- a/plugins/colorengines/lcms2/tests/TestKoCompositeOps.h
+++ b/plugins/colorengines/lcms2/tests/TestKoCompositeOps.h
@@ -1,44 +1,44 @@
/*
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KOCOMPOSITEOPS_H_
#define _KOCOMPOSITEOPS_H_
-#include <QtTest>
+#include <QObject>
class TestKoCompositeOps : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testCompositeOver();
void testCompositeAlphaDarken();
void testCompositeAdd();
void testCompositeDivide();
void testCompositeDodge();
void testCompositeInversedSubtract();
void testCompositeMulitply();
void testCompositeOverlay();
void testCompositeScreen();
void testCompositeSubtract();
void testCompositeCopy();
void testCompositeCopy2();
};
#endif
diff --git a/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.cpp b/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.cpp
index 95d7756948f..e75462c774b 100644
--- a/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.cpp
+++ b/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.cpp
@@ -1,172 +1,175 @@
#include "TestKoLcmsColorProfile.h"
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <LcmsColorProfileContainer.h>
#include <KoColor.h>
+
+#include <QTest>
+
#include <lcms2.h>
#include <cmath>
qreal testRounding(qreal value)
{
qreal factor;
int temp;
const int numPlaces = 3;
factor = pow(10.0, numPlaces);
temp = (int)(value * factor + 0.5);
return temp / factor;
}
void TestKoLcmsColorProfile::testChromaticitiesFromProfile()
{
#if 0
cmsHPROFILE profile = cmsCreate_sRGBProfile();
KoLcmsRGBColorProfile::Chromaticities chromaticities = KoLcmsRGBColorProfile::chromaticitiesFromProfile(profile);
const cmsCIExyY profileRed = {0.6400f, 0.3300f, 0.212656f};
const cmsCIExyY profileGreen = {0.3000f, 0.6000f, 0.715158f};
const cmsCIExyY profileBlue = {0.1500f, 0.0600f, 0.072186f};
const cmsCIExyY profileWhite = {0.3127f, 0.3290f, 1.000000f};
QCOMPARE(testRounding(chromaticities.primaries.Red.x), testRounding(profileRed.x));
QCOMPARE(testRounding(chromaticities.primaries.Red.y), testRounding(profileRed.y));
QCOMPARE(testRounding(chromaticities.primaries.Red.Y), testRounding(profileRed.Y));
QCOMPARE(testRounding(chromaticities.primaries.Green.x), testRounding(profileGreen.x));
QCOMPARE(testRounding(chromaticities.primaries.Green.y), testRounding(profileGreen.y));
QCOMPARE(testRounding(chromaticities.primaries.Green.Y), testRounding(profileGreen.Y));
QCOMPARE(testRounding(chromaticities.primaries.Blue.x), testRounding(profileBlue.x));
QCOMPARE(testRounding(chromaticities.primaries.Blue.y), testRounding(profileBlue.y));
QCOMPARE(testRounding(chromaticities.primaries.Blue.Y), testRounding(profileBlue.Y));
QCOMPARE(testRounding(chromaticities.whitePoint.x), testRounding(profileWhite.x));
QCOMPARE(testRounding(chromaticities.whitePoint.y), testRounding(profileWhite.y));
cmsCloseProfile(profile);
#endif
}
void TestKoLcmsColorProfile::testProfileCreationFromChromaticities()
{
#if 0
KoLcmsRGBColorProfile::Chromaticities chromaticities;
chromaticities.primaries.Red.x = 0.7347f;
chromaticities.primaries.Red.y = 0.2653f;
chromaticities.primaries.Red.Y = 1.0f;
chromaticities.primaries.Green.x = 0.1596f;
chromaticities.primaries.Green.y = 0.8404f;
chromaticities.primaries.Green.Y = 1.0f;
chromaticities.primaries.Blue.x = 0.0366f;
chromaticities.primaries.Blue.y = 0.0001f;
chromaticities.primaries.Blue.Y = 1.0f;
chromaticities.whitePoint.x = 0.34567f;
chromaticities.whitePoint.y = 0.35850f;
chromaticities.whitePoint.Y = 1.0f;
qreal gamma = 1.75f;
KoLcmsRGBColorProfile *profile = new KoLcmsRGBColorProfile(chromaticities, gamma);
QVERIFY(profile != 0);
QCOMPARE(profile->colorSpaceSignature(), icSigRgbData);
cmsHPROFILE lcmsProfile = profile->lcmsProfile();
KoLcmsRGBColorProfile::Chromaticities profileChromaticities =
KoLcmsRGBColorProfile::chromaticitiesFromProfile(lcmsProfile);
QCOMPARE(testRounding(profileChromaticities.primaries.Red.x), testRounding(chromaticities.primaries.Red.x));
QCOMPARE(testRounding(profileChromaticities.primaries.Red.y), testRounding(chromaticities.primaries.Red.y));
QCOMPARE(testRounding(profileChromaticities.primaries.Green.x), testRounding(chromaticities.primaries.Green.x));
QCOMPARE(testRounding(profileChromaticities.primaries.Green.y), testRounding(chromaticities.primaries.Green.y));
QCOMPARE(testRounding(profileChromaticities.primaries.Blue.x), testRounding(chromaticities.primaries.Blue.x));
QCOMPARE(testRounding(profileChromaticities.primaries.Blue.y), testRounding(chromaticities.primaries.Blue.y));
QCOMPARE(testRounding(profileChromaticities.whitePoint.x), testRounding(chromaticities.whitePoint.x));
QCOMPARE(testRounding(profileChromaticities.whitePoint.y), testRounding(chromaticities.whitePoint.y));
LPGAMMATABLE redGamma = cmsReadICCGamma(lcmsProfile, icSigRedTRCTag);
LPGAMMATABLE greenGamma = cmsReadICCGamma(lcmsProfile, icSigGreenTRCTag);
LPGAMMATABLE blueGamma = cmsReadICCGamma(lcmsProfile, icSigBlueTRCTag);
QCOMPARE(testRounding(cmsEstimateGamma(redGamma)), gamma);
QCOMPARE(testRounding(cmsEstimateGamma(greenGamma)), gamma);
QCOMPARE(testRounding(cmsEstimateGamma(blueGamma)), gamma);
QString expectedProfileName = QString("lcms virtual RGB profile - R(%1, %2) G(%3, %4) B(%5, %6) W(%7, %8) gamma %9")
.arg(chromaticities.primaries.Red.x)
.arg(chromaticities.primaries.Red.y)
.arg(chromaticities.primaries.Green.x)
.arg(chromaticities.primaries.Green.y)
.arg(chromaticities.primaries.Blue.x)
.arg(chromaticities.primaries.Blue.y)
.arg(chromaticities.whitePoint.x)
.arg(chromaticities.whitePoint.y)
.arg(gamma);
QCOMPARE(profile->name(), expectedProfileName);
QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), expectedProfileName);
profileChromaticities = profile->chromaticities();
QCOMPARE(profileChromaticities.primaries.Red.x, chromaticities.primaries.Red.x);
QCOMPARE(profileChromaticities.primaries.Red.y, chromaticities.primaries.Red.y);
QCOMPARE(profileChromaticities.primaries.Green.x, chromaticities.primaries.Green.x);
QCOMPARE(profileChromaticities.primaries.Green.y, chromaticities.primaries.Green.y);
QCOMPARE(profileChromaticities.primaries.Blue.x, chromaticities.primaries.Blue.x);
QCOMPARE(profileChromaticities.primaries.Blue.y, chromaticities.primaries.Blue.y);
QCOMPARE(profileChromaticities.whitePoint.x, chromaticities.whitePoint.x);
QCOMPARE(profileChromaticities.whitePoint.y, chromaticities.whitePoint.y);
const QString testProfileName = "Test Profile Name";
profile = new KoLcmsRGBColorProfile(chromaticities, gamma, testProfileName);
lcmsProfile = profile->lcmsProfile();
QCOMPARE(profile->name(), testProfileName);
QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), testProfileName);
#endif
}
void TestKoLcmsColorProfile::testConversion()
{
const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb16("sRGB built-in");
Q_ASSERT(sRgb);
const KoColorSpace *linearRgb = KoColorSpaceRegistry::instance()->rgb16("scRGB (linear)");
Q_ASSERT(linearRgb);
quint16 src[4];
src[0] = 257;
src[1] = 257;
src[2] = 257;
src[3] = 65535;
quint16 dst[4];
memset(&dst, 0, 8);
linearRgb->convertPixelsTo((quint8*)&src, (quint8*)&dst, sRgb, 1, KoColorConversionTransformation::IntentRelativeColorimetric, KoColorConversionTransformation::BlackpointCompensation);
quint16 dst2[4];
memset(&dst2, 0, 8);
cmsHPROFILE sRgbProfile = cmsCreate_sRGBProfile();
QByteArray rawData = linearRgb->profile()->rawData();
cmsHPROFILE linearRgbProfile = cmsOpenProfileFromMem((void*)rawData.constData(), rawData.size());
cmsHTRANSFORM tf = cmsCreateTransform(linearRgbProfile,
TYPE_BGRA_16,
sRgbProfile,
TYPE_BGRA_16,
INTENT_RELATIVE_COLORIMETRIC,
cmsFLAGS_NOOPTIMIZE);
cmsDoTransform(tf, (quint8*)&src, (quint8*)&dst2, 1);
Q_ASSERT(dst[0] == dst2[0]);
}
QTEST_MAIN(TestKoLcmsColorProfile)
#include <TestKoLcmsColorProfile.moc>
diff --git a/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.h b/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.h
index b3086a484c4..4f98bcd7dd6 100644
--- a/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.h
+++ b/plugins/colorengines/lcms2/tests/TestKoLcmsColorProfile.h
@@ -1,16 +1,16 @@
#ifndef TESTKOLCMSCOLORPROFILE_H
#define TESTKOLCMSCOLORPROFILE_H
-#include <QtTest>
+#include <QObject>
class TestKoLcmsColorProfile : public QObject
{
Q_OBJECT
// commented out since I don't know when...
void testChromaticitiesFromProfile();
void testProfileCreationFromChromaticities();
private Q_SLOTS:
void testConversion();
};
#endif
diff --git a/plugins/formulashape/tests/TestAttributeManager.cpp b/plugins/formulashape/tests/TestAttributeManager.cpp
index 9239a0a7d0c..fdf757d73db 100644
--- a/plugins/formulashape/tests/TestAttributeManager.cpp
+++ b/plugins/formulashape/tests/TestAttributeManager.cpp
@@ -1,28 +1,30 @@
#include "TestAttributeManager.h"
#include <BasicElement.h>
#include <AttributeManager.h>
#include <KoXmlReader.h>
+#include <QTest>
+
void TestAttributeManager::initTestCase()
{
m_attributeManager = new AttributeManager();
m_basicElement = new BasicElement( 0 );
}
void TestAttributeManager::cleanupTestCase()
{
delete m_attributeManager;
delete m_basicElement;
}
void TestAttributeManager::testColorConversion()
{
m_basicElement->setAttribute( "color", "blue" );
m_basicElement->setAttribute( "color1","green" );
m_basicElement->setAttribute( "color2","#001122" );
m_basicElement->setAttribute( "color3","transparent" );
m_basicElement->setAttribute( "color4","#123" );
}
QTEST_MAIN(TestAttributeManager)
diff --git a/plugins/formulashape/tests/TestAttributeManager.h b/plugins/formulashape/tests/TestAttributeManager.h
index 3d0bed6b341..2d9f73586a9 100644
--- a/plugins/formulashape/tests/TestAttributeManager.h
+++ b/plugins/formulashape/tests/TestAttributeManager.h
@@ -1,28 +1,27 @@
#ifndef TESTATTRIBUTEMANAGER_H
#define TESTATTRIBUTEMANAGER_H
#include <QObject>
-#include <QtTest>
class AttributeManager;
class BasicElement;
class TestAttributeManager : public QObject {
Q_OBJECT
public:
TestAttributeManager() {}
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
/// Test for correct conversion of color attributes
void testColorConversion();
private:
AttributeManager* m_attributeManager;
BasicElement* m_basicElement;
};
#endif
diff --git a/plugins/formulashape/tests/TestCursor.cpp b/plugins/formulashape/tests/TestCursor.cpp
index 746334afea6..03a4ae35fd3 100644
--- a/plugins/formulashape/tests/TestCursor.cpp
+++ b/plugins/formulashape/tests/TestCursor.cpp
@@ -1,119 +1,119 @@
/* This file is part of the KDE project
Copyright 2009 Jeremias Epperlein <jeeree@web.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestCursor.h"
-#include <qtest_kde.h>
+#include <QTest>
#include "FormulaCursor.h"
#include "FormulaData.h"
#include <KoDocument.h>
#include "FormulaCommand.h"
#include "FormulaCommandUpdate.h"
#include "KoFormulaTool.h"
#include <KoShapeManager.h>
#include <KoSelection.h>
#include <KoCanvasBase.h>
#include <KoUnit.h>
#include <kdebug.h>
#include <FormulaEditor.h>
class MockCanvas : public KoCanvasBase
{
public:
KUndo2QStack *stack;
KoShapeManager *manager;
MockCanvas(): KoCanvasBase(0) {
stack=new KUndo2QStack();
manager=new KoShapeManager(this);
}
~MockCanvas() {
delete stack;
}
void gridSize(qreal *, qreal *) const {}
bool snapToGrid() const {
return false;
}
void addCommand(KUndo2Command* c) {
// c->redo();
stack->push(c);
}
KoShapeManager *shapeManager() const {
return manager;
}
void updateCanvas(const QRectF&) {}
KoToolProxy * toolProxy() const {
return 0;
}
KoViewConverter *viewConverter() const {
return 0;
}
QWidget* canvasWidget() {
return 0;
}
const QWidget* canvasWidget() const {
return 0;
}
KoUnit unit() const {
return KoUnit(KoUnit::Millimeter);
}
void updateInputMethodInfo() {}
void setCursor(const QCursor &) {}
};
void TestCursor::moveCursor()
{
MockCanvas* canvas=new MockCanvas();
KoFormulaShape* shape = new KoFormulaShape(NULL); // FIXME: Do we need a real resourceManager here?
canvas->shapeManager()->addShape(shape);
canvas->shapeManager()->selection()->select(shape);
QCOMPARE(canvas->shapeManager()->selection()->count(),1);
KoFormulaTool* tool= new KoFormulaTool(canvas);
QSet<KoShape*> selectedShapes;
selectedShapes << shape;
tool->activate(KoToolBase::DefaultActivation, selectedShapes);
FormulaEditor* editor=tool->formulaEditor();
FormulaElement* root=editor->formulaData()->formulaElement();
canvas->addCommand(new FormulaCommandUpdate(shape,editor->insertText("ade")));
editor->cursor().moveTo(root->childElements()[0],1);
//(a|de)
canvas->addCommand(new FormulaCommandUpdate(shape,editor->insertText("bc")));
editor->cursor().moveTo(root->childElements()[0],6);
//(abcde|)
editor->cursor().move(MoveLeft);
//(abcd|e)
QCOMPARE(editor->cursor().position(),5);
editor->cursor().moveTo(root->childElements()[0],0);
editor->cursor().move(MoveLeft);
//|(abcde)
QCOMPARE(editor->cursor().position(),0);
canvas->addCommand(new FormulaCommandUpdate(shape,editor->insertText("123")));
QCOMPARE(root->childElements().count(),2);
//(12)(abcde)
canvas->stack->undo();
//(abcde)
canvas->stack->redo();
//(12)(abcde)
QCOMPARE(root->childElements().count(),2);
canvas->stack->clear();
}
-QTEST_KDEMAIN(TestCursor,GUI)
+QTEST_MAIN(TestCursor)
diff --git a/plugins/formulashape/tests/TestLayout.cpp b/plugins/formulashape/tests/TestLayout.cpp
index 0f04de89c1c..f12e0d95ad6 100644
--- a/plugins/formulashape/tests/TestLayout.cpp
+++ b/plugins/formulashape/tests/TestLayout.cpp
@@ -1,114 +1,113 @@
/* This file is part of the KDE project
Copyright 2007 Alfredo Beaumont Sainz <alfredo.beaumont@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestLayout.h"
-#include <qtest_kde.h>
+#include <QTest>
-#include <QtTest>
#include <QFont>
#include <QFontMetrics>
#include "AttributeManager.h"
#include "IdentifierElement.h"
#include "FencedElement.h"
#include <KoXmlReader.h>
static QRectF layout(BasicElement* element, const QString& input)
{
KoXmlDocument doc;
doc.setContent( input );
element->readMathML(doc.documentElement());
AttributeManager am;
element->layout( &am );
return element->boundingRect();
}
static void addRowInternal( const QString& input, const QString& text, const QFont& font )
{
QFontMetrics fm( font );
QTest::newRow( "Layout" ) << input << QRectF( fm.boundingRect( text ) );
}
static void addRow( const QString& input, const QString& text )
{
QFont font;
addRowInternal( input, text, font );
}
static void addRow( const QString& input, const QString& text, double size )
{
QFont font;
font.setPointSizeF( size );
addRowInternal( input, text, font);
}
void TestLayout::identifierElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QRectF>("output");
addRow( "<mi>x</mi>",
"x");
addRow( "<mi fontsize=\"12pt\">x</mi>",
"x", 12);
}
void TestLayout::identifierElement()
{
QFETCH(QString, input);
QFETCH(QRectF, output);
IdentifierElement* element = new IdentifierElement;
QCOMPARE(layout(element, input), output);
delete element;
}
void TestLayout::fencedElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QRectF>("output");
addRow( "<mfenced></mfenced>",
"()");
addRow( "<mfenced><mi>x</mi></mfenced>",
"(x)");
addRow( "<mfenced><mi>x</mi><mi>y</mi></mfenced>",
"(x,y)");
addRow( "<mfenced open=\"[\"></mfenced>",
"[)");
addRow( "<mfenced open=\"[\" close=\"}\"></mfenced>",
"[}");
addRow( "<mfenced open=\"[\" close=\"}\"></mfenced>",
"[}");
addRow( "<mfenced separators=\";.\"><mi>x</mi><mi>y</mi><mi>z</mi></mfenced>",
"(x;y.z)");
}
void TestLayout::fencedElement()
{
QFETCH(QString, input);
QFETCH(QRectF, output);
FencedElement* element = new FencedElement;
QCOMPARE(layout(element, input), output);
delete element;
}
-QTEST_KDEMAIN(TestLayout, GUI)
+QTEST_MAIN(TestLayout)
diff --git a/plugins/formulashape/tests/TestLoad.cpp b/plugins/formulashape/tests/TestLoad.cpp
index 57fc274289c..3938abe1581 100644
--- a/plugins/formulashape/tests/TestLoad.cpp
+++ b/plugins/formulashape/tests/TestLoad.cpp
@@ -1,1056 +1,1056 @@
/* This file is part of the KDE project
Copyright 2007 Alfredo Beaumont Sainz <alfredo.beaumont@gmail.com>
Copyright 2010 Inge wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestLoad.h"
-#include <QtTest>
+#include <QTest>
#include <QBuffer>
#include <KoXmlReader.h>
#include "BasicElement.h"
#include "IdentifierElement.h"
#include "NumberElement.h"
#include "OperatorElement.h"
#include "TextElement.h"
#include "SpaceElement.h"
#include "StringElement.h"
#include "GlyphElement.h"
#include "RowElement.h"
#include "FractionElement.h"
#include "RootElement.h"
#include "StyleElement.h"
#include "ErrorElement.h"
#include "PaddedElement.h"
#include "PhantomElement.h"
#include "FencedElement.h"
#include "EncloseElement.h"
#include "MultiscriptElement.h"
#include "UnderOverElement.h"
#include "SubSupElement.h"
#include "TableElement.h"
#include "TableRowElement.h"
#include "TableDataElement.h"
#include "ActionElement.h"
static void load(BasicElement* element, const QString& input)
{
KoXmlDocument doc;
doc.setContent( input );
element->readMathML(doc.documentElement());
}
static int count( const QList<BasicElement*>& list )
{
BasicElement* element;
int counter = list.count();
foreach ( element, list )
counter += count( element->childElements() );
return counter;
}
static QString dumpRecurse(const QList<BasicElement*>& list)
{
BasicElement *element;
QString result = "[ ";
if (list.count() > 0) {
result.append(QString::number(list.count()));
result.append(' ');
foreach ( element, list )
result.append(dumpRecurse(element->childElements()));
}
return result + " ]";
}
//static void dump( const QList<BasicElement*>& list )
//{
// qDebug() << dumpRecurse(list);
//}
static void addRow( const QString& input, int output )
{
static int counter = 0;
QString name = "Load " + QString::number( ++counter );
QTest::newRow( name.toLatin1() ) << input << output << output;
}
static void addRow( const QString& input, int output, int outputRecursive )
{
static int counter = 0;
QString name = "LoadRecursive " + QString::number( ++counter );
QTest::newRow( name.toLatin1() ) << input << output << outputRecursive;
}
void test( BasicElement* element )
{
QFETCH(QString, input);
QFETCH(int, output);
QFETCH(int, outputRecursive);
load( element, input );
//element->writeElementTree();
int numElements = count( element->childElements() );
#if 0 // Set to 1 if you want to dump the xml tree if the test fails.
if (numElements != outputRecursive) {
qDebug() << input;
//dump(element->childElements());
element->writeElementTree();
}
#endif
QCOMPARE( element->childElements().count() , output );
QCOMPARE( numElements, outputRecursive );
delete element;
}
void TestLoad::identifierElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Empty content
addRow( "<mi></mi>", 0 );
// Basic content
addRow( "<mi>x</mi>", 0 );
addRow( "<mi>sin</mi>", 0 );
// Glyph element contents
addRow( "<mi>x<mglyph fontfamily=\"serif\" alt=\"a\" index=\"97\"/></mi>", 1);
addRow( "<mi> <mglyph fontfamily=\"serif\" alt=\"sin\" index=\"97\"/> </mi>", 1);
addRow( "<mi> <mglyph fontfamily=\"serif\" alt=\"x\" index=\"97\"/> "
" <mglyph fontfamily=\"serif\" alt=\"y\" index=\"97\"/> </mi>", 2);
// Be sure attributes don't break anything
addRow( "<mi mathvariant=\"bold\">x</mi>", 0 );
addRow( "<mi fontsize=\"18pt\">x</mi>", 0 );
// Be sure content with entity references don't break anything
addRow( "<mi> &pi; </mi>", 0 );
addRow( "<mi> &ImaginaryI; </mi>", 0 );
}
void TestLoad::numberElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mn> 3 </mn>", 0 );
addRow( "<mn> 1,000,000.11 </mn>", 0 );
addRow( "<mn> 1.000.000,11 </mn>", 0 );
// Glyph element contents
addRow( "<mn>12<mglyph fontfamily=\"serif\" alt=\"8\" index=\"56\"/></mn>", 1);
addRow( "<mn> <mglyph fontfamily=\"serif\" alt=\"8\" index=\"56\"/> </mn>", 1);
addRow( "<mn> <mglyph fontfamily=\"serif\" alt=\"8\" index=\"56\"/> "
" <mglyph fontfamily=\"serif\" alt=\"7\" index=\"55\"/> </mn>", 2);
// Be sure attributes don't break anything
addRow( "<mn mathvariant=\"bold\">1</mn>", 0 );
addRow( "<mn fontsize=\"18pt\">1</mn>", 0 );
}
void TestLoad::operatorElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mo>+</mo>", 0 );
addRow( "<mo> ++ </mo>", 0 );
// Glyph element contents
addRow( "<mo>+<mglyph fontfamily=\"serif\" alt=\"+\" index=\"43\"/></mo>", 1);
addRow( "<mo> <mglyph fontfamily=\"serif\" alt=\"+\" index=\"43\"/> </mo>", 1);
addRow( "<mo> <mglyph fontfamily=\"serif\" alt=\"+\" index=\"43\"/> "
" <mglyph fontfamily=\"serif\" alt=\"=\" index=\"61\"/> </mo>", 2);
// Be sure attributes don't break anything
addRow( "<mo mathvariant=\"bold\">+</mo>", 0 );
addRow( "<mo fontsize=\"18pt\">+</mo>", 0 );
// Be sure content with entity references don't break anything
addRow( "<mo> &sum; </mo>", 0 );
addRow( "<mo> &InvisibleTimes; </mo>", 0 );
}
void TestLoad::textElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mtext>text</mtext>", 0 );
addRow( "<mtext> more text </mtext>", 0 );
// Glyph element contents
addRow( "<mtext>tex<mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/></mtext>", 1);
addRow( "<mtext> <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </mtext>", 1);
addRow( "<mtext>te <mglyph fontfamily=\"serif\" alt=\"x\" index=\"120\"/> "
" <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </mtext>", 2);
// Be sure attributes don't break anything
addRow( "<mtext mathvariant=\"bold\">text</mtext>", 0 );
addRow( "<mtext fontsize=\"18pt\">text</mtext>", 0 );
// Be sure content with entity references don't break anything
addRow( "<mtext> &ThinSpace; </mtext>", 0 );
addRow( "<mtext> &ThinSpace;&ThickSpace; </mtext>", 0 );
}
void TestLoad::spaceElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Space element does not have content
addRow( "<mspace/>", 0 );
addRow( "<mspace width=\0.5em\"/>", 0 );
addRow( "<mspace linebreak=\"newline\"/>", 0 );
}
void TestLoad::stringElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<ms>text</ms>", 0 );
addRow( "<ms> more text </ms>", 0 );
// Glyph element contents
addRow( "<ms>tex<mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/></ms>", 1);
addRow( "<ms> <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>", 1);
addRow( "<ms>te <mglyph fontfamily=\"serif\" alt=\"x\" index=\"120\"/> "
" <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>", 2);
// Be sure attributes don't break anything
addRow( "<ms mathvariant=\"bold\">text</ms>", 0 );
addRow( "<ms fontsize=\"18pt\">text</ms>", 0 );
// Be sure content with entity references don't break anything
addRow( "<ms> &amp; </ms>", 0 );
addRow( "<ms> &amp;amp; </ms>", 0 );
}
void TestLoad::glyphElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Glyph element does not have content
addRow( "<mglyph fontfamily=\"serif\" index=\"97\" alt=\"a\"/>", 0 );
}
void TestLoad::rowElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mrow></mrow>", 0 );
addRow( "<mrow><mi>x</mi></mrow>", 1 );
addRow( "<mrow><mi>x</mi><mo>=</mo><mn>3</mn></mrow>", 3 );
// More complex content
addRow( "<mrow><mrow></mrow></mrow>", 0 );
addRow( "<mrow><mrow><mi>x</mi></mrow></mrow>", 1 );
addRow( "<mrow><mrow><mi>x</mi><mn>2</mn></mrow></mrow>", 1, 3 ); // Keep mrow with >1 children
addRow( "<mrow>"
" <mrow>"
" <mn> 2 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <mi> x </mi>"
" </mrow>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> - </mo>"
" <mi> z </mi>"
"</mrow>", 5, 8 );
addRow( "<mrow>"
" <mo> ( </mo>"
" <mrow>"
" <mi> x </mi>"
" <mo> , </mo>"
" <mi> y </mi>"
" </mrow>"
" <mo> ) </mo>"
"</mrow>", 3, 6 );
}
void TestLoad::fracElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mfrac><mi>x</mi><mi>y</mi></mfrac>", 2, 4 ); // +1 <mrow> for both sides
// More complex content
addRow( "<mfrac linethickness=\"2\">"
" <mfrac>"
" <mi> a </mi>"
" <mi> b </mi>"
" </mfrac>"
" <mfrac>"
" <mi> c </mi>"
" <mi> d </mi>"
" </mfrac>"
"</mfrac>", 2, 6 + 6 ); // +2 mrow per mfrac
addRow( "<mfrac>"
" <mn> 1 </mn>"
" <mrow>"
" <msup>"
" <mi> x </mi>"
" <mn> 3 </mn>"
" </msup>"
" <mo> + </mo>"
" <mfrac>"
" <mi> x </mi>"
" <mn> 3 </mn>"
" </mfrac>"
" </mrow>"
"</mfrac>", 2, 9 + 5 ); // +2 mrow per mfrac, and msup -1 for the one in the test
}
void TestLoad::rootElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
/* addRow( "<msqrt></msqrt>", 1 );
addRow( "<msqrt><mrow></mrow></msqrt>", 1 );
addRow( "<msqrt><mi>x</mi></msqrt>", 1, 2 );
addRow( "<msqrt><mrow><mi>x</mi></mrow></msqrt>", 2, 2 );*/
addRow( "<mroot><mi>x</mi><mn>2</mn></mroot>", 2, 4 );
// More complex content
/* addRow( "<msqrt>"
" <mi> x </mi>"
" <mroot>"
" <mrow>"
" <mn> 2 </mn>"
" <mo> &InvisibleTimes </mn>"
" <mi> y </mi>"
" </mrow>"
" <mfrac>"
" <mn> 1 </mn>"
" <mn> 2 </mn>"
" </frac>"
" </mroot>"
"</msqrt", 1, 13 );*/
}
void TestLoad::styleElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mstyle></mstyle>", 0 );
addRow( "<mstyle><mrow></mrow></mstyle>", 0 );
addRow( "<mstyle><mi>x</mi></mstyle>", 1 );
addRow( "<mstyle><mrow><mi>x</mi></mrow></mstyle>", 1 );
// Be sure attributes don't break anything
addRow( "<mstyle mathvariant=\"bold\"><mi>x</mi></mstyle>", 1 );
addRow( "<mstyle thinmathspace=\"0.5em\"><mi>x</mi></mstyle>", 1 );
}
void TestLoad::errorElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<merror></merror>", 0 );
addRow( "<merror><mrow></mrow></merror>", 0 );
addRow( "<merror><mi>x</mi></merror>", 1 );
addRow( "<merror><mrow><mi>x</mi></mrow></merror>", 1 );
// More complex content
addRow( "<merror>"
" <mtext> Unrecognized element: mfraction;"
" arguments were: "
" </mtext>"
" <mrow> <mn> 1 </mn> <mo> + </mo> <msqrt> <mn> 5 </mn> </msqrt> </mrow>"
" <mtext> and </mtext>"
" <mn> 2 </mn>"
"</merror>", 4, 8 );
}
void TestLoad::paddedElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mpadded></mpadded>", 0);
addRow( "<mpadded><mrow></mrow></mpadded>", 0 );
addRow( "<mpadded><mi>x</mi></mpadded>", 1 );
addRow( "<mpadded><mrow><mi>x</mi></mrow></mpadded>", 1 );
// Be sure attributes don't break anything
addRow( "<mpadded width=\"+0.8em\"><mi>x</mi></mpadded>", 1 );
addRow( "<mpadded depth=\"1.2\"><mi>x</mi></mpadded>", 1 );
}
void TestLoad::phantomElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mphantom></mphantom>", 0 );
addRow( "<mphantom><mrow></mrow></mphantom>", 0 );
addRow( "<mphantom><mi>x</mi></mphantom>", 1 );
addRow( "<mphantom><mrow><mi>x</mi></mrow></mphantom>", 1 );
// Be sure attributes don't break anything
addRow( "<mphantom width=\"+0.8em\"><mi>x</mi></mphantom>", 1 );
addRow( "<mphantom depth=\"1.2\"><mi>x</mi></mphantom>", 1 );
}
void TestLoad::fencedElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// This is an inferred mrow element
addRow( "<mfenced></mfenced>", 0 );
addRow( "<mfenced><mi>x</mi></mfenced>", 1 );
addRow( "<mfenced><mi>x</mi><mn>2</mn></mfenced>", 2 ); // Inferred mrow
}
void TestLoad::encloseElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<menclose></menclose>", 0 );
addRow( "<menclose><mrow></mrow></menclose>", 0 );
addRow( "<menclose><mi>x</mi></menclose>", 1 );
addRow( "<menclose><mrow><mi>x</mi></mrow></menclose>", 1 );
// Be sure attributes don't break anything
addRow( "<menclose notation=\"longdiv\"><mi>x</mi></menclose>", 1 );
addRow( "<menclose notation=\"downdiagonalstrike\"><mi>x</mi></menclose>", 1 );
}
void TestLoad::subElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<msub><mrow></mrow><mrow></mrow></msub>", 2 );
addRow( "<msub><mi>x</mi><mi>y</mi></msub>", 2, 4 );
addRow( "<msub><mrow><mi>x</mi></mrow><mi>y</mi></msub>", 2, 4 );
addRow( "<msub><mi>x</mi><mrow><mi>y</mi></mrow></msub>", 2, 4 );
addRow( "<msub><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></msub>", 2, 4 );
// More complex content
addRow( "<msub>"
" <mrow>"
" <mo> ( </mo>"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" </mrow>"
" <mo> ) </mo>"
" </mrow>"
" <mn> 2 </mn>" // An mrow is added here
"</msub>", 2, 9 );
// Be sure attributes don't break anything
addRow( "<msub subscriptshift=\"1.5ex\"><mi>x</mi><mi>y</mi></msub>", 2, 4 );
addRow( "<msub subscriptshift=\"1.5\"><mi>x</mi><mi>y</mi></msub>", 2, 4 );
}
void TestLoad::supElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<msup><mrow></mrow><mrow></mrow></msup>", 2 );
addRow( "<msup><mi>x</mi><mi>y</mi></msup>", 2, 4 );
addRow( "<msup><mrow><mi>x</mi></mrow><mi>y</mi></msup>", 2, 4 );
addRow( "<msup><mi>x</mi><mrow><mi>y</mi></mrow></msup>", 2, 4 );
addRow( "<msup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></msup>", 2, 4 );
// More complex content
addRow( "<msup>"
" <mrow>"
" <mo> ( </mo>"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" </mrow>"
" <mo> ) </mo>"
" </mrow>"
" <mn> 2 </mn>" // An mrow is added here
"</msup>", 2, 9 );
// Be sure attributes don't break anything
addRow( "<msup superscriptshift=\"1.5ex\"><mi>x</mi><mi>y</mi></msup>", 2, 4 );
addRow( "<msup superscriptshift=\"1.5\"><mi>x</mi><mi>y</mi></msup>", 2, 4 );
}
void TestLoad::subsupElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<msubsup><mrow></mrow><mrow></mrow><mrow></mrow></msubsup>", 3 );
addRow( "<msubsup><mi>x</mi><mi>y</mi><mi>z</mi></msubsup>", 3, 6 );
addRow( "<msubsup><mrow><mi>x</mi></mrow><mi>y</mi><mi>z</mi></msubsup>", 3, 6 );
addRow( "<msubsup><mi>x</mi><mrow><mi>y</mi></mrow><mi>z</mi></msubsup>", 3, 6 );
addRow( "<msubsup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow><mi>z</mi></msubsup>", 3, 6 );
addRow( "<msubsup><mrow><mi>x</mi></mrow><mi>y</mi><mrow><mi>z</mi></mrow></msubsup>", 3, 6 );
addRow( "<msubsup><mi>x</mi><mrow><mi>y</mi></mrow><mrow><mi>z</mi></mrow></msubsup>", 3, 6 );
addRow( "<msubsup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow><mrow><mi>z</mi></mrow></msubsup>", 3, 6 );
// Be sure attributes don't break anything
addRow( "<msubsup subscriptshift=\"1.5ex\"><mi>x</mi><mi>y</mi><mi>z</mi></msubsup>", 3, 6 );
addRow( "<msubsup superscriptshift=\"1.5ex\"><mi>x</mi><mi>y</mi><mi>z</mi></msubsup>", 3, 6 );
}
void TestLoad::underElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<munder><mrow></mrow><mrow></mrow></munder>", 2 );
addRow( "<munder><mi>x</mi><mi>y</mi></munder>", 2, 4 );
addRow( "<munder><mrow><mi>x</mi></mrow><mi>y</mi></munder>", 2, 4 );
addRow( "<munder><mi>x</mi><mrow><mi>y</mi></mrow></munder>", 2, 4 );
addRow( "<munder><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></munder>", 2, 4 );
// More complex content
addRow( "<munder accentunder=\"true\">"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> + </mo>"
" <mi> z </mi>"
" </mrow>"
" <mo> &UnderBrace; </mo>"
"</munder>", 2, 8);
addRow( "<munder accentunder=\"false\">"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> + </mo>"
" <mi> z </mi>"
" </mrow>"
" <mo> &UnderBrace; </mo>"
"</munder>", 2, 8 );
// Be sure attributes don't break anything
addRow( "<munder accentunder=\"true\"><mi>x</mi><mi>y</mi></munder>", 2, 4 );
addRow( "<munder accentunder=\"false\"><mi>x</mi><mi>y</mi></munder>", 2, 4 );
}
void TestLoad::overElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mover><mrow></mrow><mrow></mrow></mover>", 2 );
addRow( "<mover><mi>x</mi><mi>y</mi></mover>", 2, 4 );
addRow( "<mover><mrow><mi>x</mi></mrow><mi>y</mi></mover>", 2, 4 );
addRow( "<mover><mi>x</mi><mrow><mi>y</mi></mrow></mover>", 2, 4 );
addRow( "<mover><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></mover>", 2, 4 );
// More complex content
addRow( "<mover accent=\"true\">"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> + </mo>"
" <mi> z </mi>"
" </mrow>"
" <mo> &OverBrace; </mo>"
"</mover>", 2, 8);
addRow( "<mover accent=\"false\">"
" <mrow>"
" <mi> x </mi>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> + </mo>"
" <mi> z </mi>"
" </mrow>"
" <mo> &OverBrace; </mo>"
"</mover>", 2, 8 );
// Be sure attributes don't break anything
addRow( "<mover accent=\"true\"><mi>x</mi><mi>y</mi></mover>", 2, 4 );
addRow( "<mover accent=\"false\"><mi>x</mi><mi>y</mi></mover>", 2, 4 );
}
void TestLoad::underOverElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<munderover><mrow></mrow><mrow></mrow><mrow></mrow></munderover>", 3 );
addRow( "<munderover><mi>x</mi><mi>y</mi><mi>z</mi></munderover>", 3, 6 );
addRow( "<munderover><mrow><mi>x</mi></mrow><mi>y</mi><mi>z</mi></munderover>", 3, 6 );
addRow( "<munderover><mi>x</mi><mrow><mi>y</mi></mrow><mi>z</mi></munderover>", 3, 6 );
addRow( "<munderover><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow><mi>z</mi></munderover>", 3, 6 );
addRow( "<munderover><mrow><mi>x</mi></mrow><mi>y</mi><mrow><mi>z</mi></mrow></munderover>", 3, 6 );
addRow( "<munderover><mi>x</mi><mrow><mi>y</mi></mrow><mrow><mi>z</mi></mrow></munderover>", 3, 6 );
addRow( "<munderover><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow><mrow><mi>z</mi></mrow></munderover>", 3, 6 );
// Be sure attributes don't break anything
addRow( "<munderover accent=\"true\"><mi>x</mi><mi>y</mi><mi>z</mi></munderover>", 3, 6 );
addRow( "<munderover accentunder=\"false\"><mi>x</mi><mi>y</mi><mi>z</mi></munderover>", 3, 6 );
}
void TestLoad::multiscriptsElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mmultiscripts><mi>x</mi><mi>i</mi><mi>j</mi></mmultiscripts>", 3, 3 );
addRow( "<mmultiscripts><mi>x</mi><mprescripts/><mi>i</mi><mi>j</mi></mmultiscripts>", 3, 3 );
addRow( "<mmultiscripts><mi>x</mi><mi>i</mi><none/></mmultiscripts>", 2 );
addRow( "<mmultiscripts><mi>x</mi><none/><none/></mmultiscripts>", 1 );
addRow( "<mmultiscripts><mi>x</mi><none/><none/></mmultiscripts>", 1 ); // ?? Same as above
addRow( "<mmultiscripts><mi>x</mi><mprescripts/><none/><none/></mmultiscripts>", 1 );
addRow( "<mmultiscripts><mi>x</mi><none/><none/><mprescripts/><none/><none/></mmultiscripts>", 1 );
addRow( "<mmultiscripts><mi>x</mi><mi>x</mi><none/><mprescripts/><mi>y</mi><none/></mmultiscripts>", 3 );
// More complex content
addRow( "<mmultiscripts>"
" <mi> R </mi>"
" <mi> i </mi>"
" <none/>"
" <none/>"
" <mi> j </mi>"
" <mi> k </mi>"
" <none/>"
" <mi> l </mi>"
" <none/>"
"</mmultiscripts>", 5 );
}
void TestLoad::tableElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mtable></mtable>", 0 );
addRow( "<mtable><mtr></mtr></mtable>", 1 );
addRow( "<mtable><mtr><mtd></mtd></mtr></mtable>", 1, 2 );
addRow( "<mtable><mtr><mtd><mrow></mrow></mtd></mtr></mtable>", 1, 2 ); // mtd is an inferred mrow
addRow( "<mtable><mtr><mtd><mrow><mi>x</mi></mrow></mtd></mtr></mtable>", 1, 3 );
// addRow( "<mtable><mlabeledtr><mrow></mrow></mlabeledtr></mtable>", 1, 2 );
// addRow( "<mtable><mlabeledtr><mrow></mrow><mtd></mtd></mlabeledtr></mtable>", 1, 4 );
// More complex content
addRow( "<mtable>"
" <mtr>"
" <mtd> <mn>1</mn> </mtd>"
" <mtd> <mn>0</mn> </mtd>"
" <mtd> <mn>0</mn> </mtd>"
" </mtr>"
" <mtr>"
" <mtd> <mn>0</mn> </mtd>"
" <mtd> <mn>1</mn> </mtd>"
" <mtd> <mn>0</mn> </mtd>"
" </mtr>"
" <mtr>"
" <mtd> <mn>0</mn> </mtd>"
" <mtd> <mn>0</mn> </mtd>"
" <mtd> <mn>1</mn> </mtd>"
" </mtr>"
"</mtable>", 3, 21 );
// Be sure attributes don't break anything
addRow( "<mtable align=\"top\"><mtr><mtd><mi>x</mi></mtd></mtr></mtable>", 1, 3 );
addRow( "<mtable rowalign=\"center\"><mtr><mtd><mi>x</mi></mtd></mtr></mtable>", 1, 3 );
// Content with alignment elements
/* addRow( "<mtable groupalign=\"{decimalpoint left left decimalpoint left left decimalpoint}\">"
" <mtr>"
" <mtd>"
" <mrow>"
" <mrow>"
" <mrow>"
" <maligngroup/>"
" <mn> 8.44 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> x </mi>"
" </mrow>"
" <maligngroup/>"
" <mo> + </mo>"
" <mrow>"
" <maligngroup/>"
" <mn> 55 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> y </mi>"
" </mrow>"
" </mrow>"
" <maligngroup/>"
" <mo> = </mo>"
" <maligngroup/>"
" <mn> 0 </mn>"
" </mrow>"
" </mtd>"
" </mtr>"
" <mtr>"
" <mtd>"
" <mrow>"
" <mrow>"
" <mrow>"
" <maligngroup/>"
" <mn> 3.1 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> x </mi>"
" </mrow>"
" <maligngroup/>"
" <mo> - </mo>"
" <mrow>"
" <maligngroup/>"
" <mn> 0.7 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> y </mi>"
" </mrow>"
" </mrow>"
" <maligngroup/>"
" <mo> = </mo>"
" <maligngroup/>"
" <mrow>"
" <mo> - </mo>"
" <mn> 1.1 </mn>"
" </mrow>"
" </mrow>"
" </mtd>"
" </mtr>"
"</mtable>", 2, 32 );*/
}
void TestLoad::trElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mtr></mtr>", 0 );
addRow( "<mtr><mtd></mtd></mtr>", 1, 1 );
addRow( "<mtr><mtd><mrow></mrow></mtd></mtr>", 1, 1 ); // <mtd> is an inferred <mrow>
addRow( "<mtr><mtd><mi>x</mi></mtd></mtr>", 1, 2 );
addRow( "<mtr><mtd><mrow><mi>x</mi></mrow></mtd></mtr>", 1, 2 );
// More complex content
addRow( "<mtr id='e-is-m-c-square'>"
" <mtd>"
" <mrow>"
" <mi>E</mi>"
" <mo>=</mo>"
" <mrow>"
" <mi>m</mi>"
" <mo>&it;</mo>"
" <msup>"
" <mi>c</mi>"
" <mn>2</mn>"
" </msup>"
" </mrow>"
" </mrow>"
" </mtd>"
" <mtd>"
" <mtext> (2.1) </mtext>"
" </mtd>"
"</mtr>", 2, 14 );
// Be sure attributes don't break anything
addRow( "<mtr rowalign=\"top\"><mtd><mi>x</mi></mtd></mtr>", 1, 2 );
addRow( "<mtr groupalign=\"left\"><mtd><mi>x</mi></mtd></mtr>", 1, 2 );
}
/*
void TestLoad::labeledtrElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mlabeledtr><mrow></mrow></mlabeledtr>", 1 );
addRow( "<mlabeledtr><mi>x</mi></mlabeledtr>", 1, 2 );
addRow( "<mlabeledtr><mrow></mrow><mtd></mtd></mlabeledtr>", 2, 3 );
addRow( "<mlabeledtr><mi>x</mi><mtd><mtd></mlabeledtr>", 2, 4 );
addRow( "<mlabeledtr><mrow><mi>x</mi></mrow><mtd><mrow></mrow></mtd></mlabeledtr>", 2, 4 );
addRow( "<mlabeledtr><mrow><mi>x</mi></mrow><mtd><mi>x</mi></mtd></mlabeledtr>", 2, 5 );
addRow( "<mlabeledtr><mrow><mi>x</mi></mrow><mtd><mrow><mi>x</mi></mrow></mtd></mlabeledtr>", 2, 5 );
// More complex ccontent
addRow( "<mlabeledtr id='e-is-m-c-square'>"
" <mtd>"
" <mtext> (2.1) </mtext>"
" </mtd>"
" <mtd>"
" <mrow>"
" <mi>E</mi>"
" <mo>=</mo>"
" <mrow>"
" <mi>m</mi>"
" <mo>&it;</mo>"
" <msup>"
" <mi>c</mi>"
" <mn>2</mn>"
" </msup>"
" </mrow>"
" </mrow>"
" </mtd>"
"</mlabeledtr>", 2, 15 );
// Be sure attributes don't break anything
addRow( "<mlabeledtr rowalign=\"top\"><mi>x</mi></mlabeledtr>", 1, 2 );
addRow( "<mlabeledtr groupalign=\"left\"><mi>x</mi></mlabeledtr>", 1, 2 );
}
*/
void TestLoad::tdElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<mtd></mtd>", 0 );
addRow( "<mtd><mrow></mrow></mtd>", 0, 0 ); // Empty mrow is deleted
addRow( "<mtd><mi>x</mi></mtd>", 1, 1 );
addRow( "<mtd><mrow><mi>x</mi></mrow></mtd>", 1, 1 ); // mrow with one element is deleted
// Be sure attributes don't break anything
addRow( "<mtd rowspan=\"3\"><mi>x</mi></mtd>", 1, 1 );
addRow( "<mtd groupalign=\"left\"><mi>x</mi></mtd>", 1, 1 );
}
void TestLoad::actionElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<int>("output");
QTest::addColumn<int>("outputRecursive");
// Basic content
addRow( "<maction actiontype=\"toggle\" selection=\"positive-integer\"><mrow></mrow><mrow></mrow></maction>", 0 ); //
addRow( "<maction actiontype=\"statusline\"><mrow></mrow><mrow></mrow></maction>", 0 );
addRow( "<maction actiontype=\"tooltip\"><mrow></mrow><mrow></mrow></maction>", 0 );
addRow( "<maction actiontype=\"highlight\" my:color=\"red\" my:background=\"yellow\"><mrow></mrow></maction>", 0 );
}
void TestLoad::identifierElement()
{
test( new IdentifierElement );
}
void TestLoad::numberElement()
{
test( new NumberElement );
}
void TestLoad::operatorElement()
{
test( new OperatorElement );
}
void TestLoad::textElement()
{
test( new TextElement );
}
void TestLoad::spaceElement()
{
test( new SpaceElement );
}
void TestLoad::stringElement()
{
test( new StringElement );
}
void TestLoad::glyphElement()
{
test( new GlyphElement );
}
void TestLoad::rowElement()
{
test( new RowElement );
}
void TestLoad::fracElement()
{
test( new FractionElement );
}
void TestLoad::rootElement()
{
test( new RootElement );
}
void TestLoad::styleElement()
{
test( new StyleElement );
}
void TestLoad::errorElement()
{
test( new ErrorElement );
}
void TestLoad::paddedElement()
{
test( new PaddedElement );
}
void TestLoad::phantomElement()
{
test( new PhantomElement );
}
void TestLoad::fencedElement()
{
test( new FencedElement );
}
void TestLoad::encloseElement()
{
test( new EncloseElement );
}
void TestLoad::subElement()
{
test( new SubSupElement(0, SubScript) );
}
void TestLoad::supElement()
{
test( new SubSupElement(0, SupScript) );
}
void TestLoad::subsupElement()
{
test( new SubSupElement(0, SubSupScript) );
}
void TestLoad::underElement()
{
test( new UnderOverElement(0, Under) );
}
void TestLoad::overElement()
{
test( new UnderOverElement(0, Over) );
}
void TestLoad::underOverElement()
{
test( new UnderOverElement(0, UnderOver) );
}
void TestLoad::multiscriptsElement()
{
test( new MultiscriptElement );
}
void TestLoad::tableElement()
{
test( new TableElement );
}
void TestLoad::trElement()
{
test( new TableRowElement );
}
/*
void TestLoad::labeledtrElement()
{
test( new TableRowElement );
}
*/
void TestLoad::tdElement()
{
test( new TableDataElement );
}
void TestLoad::actionElement()
{
test( new ActionElement );
}
QTEST_MAIN(TestLoad)
diff --git a/plugins/formulashape/tests/TestLoadAndSave.cpp b/plugins/formulashape/tests/TestLoadAndSave.cpp
index a110d1bf5bd..01ac0c17e48 100644
--- a/plugins/formulashape/tests/TestLoadAndSave.cpp
+++ b/plugins/formulashape/tests/TestLoadAndSave.cpp
@@ -1,1529 +1,1529 @@
/* This file is part of the KDE project
Copyright 2007-2009 Alfredo Beaumont Sainz <alfredo.beaumont@gmail.com>
Copyright 2010 Inge wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestLoadAndSave.h"
-#include <QtTest>
+#include <QTest>
#include <QBuffer>
#include <QDebug>
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
#include "BasicElement.h"
#include "IdentifierElement.h"
#include "NumberElement.h"
#include "OperatorElement.h"
#include "TextElement.h"
#include "SpaceElement.h"
#include "StringElement.h"
#include "GlyphElement.h"
#include "RowElement.h"
#include "FractionElement.h"
#include "RootElement.h"
#include "StyleElement.h"
#include "ErrorElement.h"
#include "PaddedElement.h"
#include "PhantomElement.h"
#include "FencedElement.h"
#include "EncloseElement.h"
#include "MultiscriptElement.h"
#include "SubSupElement.h"
#include "UnderOverElement.h"
#include "TableElement.h"
#include "TableRowElement.h"
#include "TableDataElement.h"
#include "ActionElement.h"
static QString loadAndSave( BasicElement* element, const QString& input )
{
KoXmlDocument doc;
doc.setContent( input );
element->readMathML( doc.documentElement() );
QBuffer device;
device.open( QBuffer::ReadWrite );
KoXmlWriter writer( &device );
element->writeMathML( &writer, "" );
device.seek( 0 );
return device.readAll();
}
static void addRow( const char* input, bool expectedFail = false )
{
QString inputStr(input);
QTest::newRow("Load and Save") << inputStr << inputStr << expectedFail;
}
static void addRow( const char* input, const char* output, bool expectedFail = false )
{
QString inputStr(input);
QString outputStr(output);
QTest::newRow("Load and Save") << inputStr << outputStr << expectedFail;
}
void test( BasicElement* element )
{
QFETCH( QString, input );
QFETCH( QString, output );
QFETCH( bool, expectedFail );
//qDebug() << "expected Fail: " << expectedFail;
if (expectedFail) {
QEXPECT_FAIL("", "", Continue);
}
QCOMPARE( loadAndSave( element, input ), output );
delete element;
}
void TestLoadAndSave::identifierElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mi>x</mi>" );
addRow( "<mi>abc</mi>" );
addRow( "<MI>x</MI>",
"<mi>x</mi>" );
// See section 2.4.6 Collapsing Whitespace in Input
addRow( "<mi> a b c </mi>",
"<mi>a b c</mi>" );
// Since newline is hardcoded in KoXmlWriter and it's sematically equivalent, add it to expected result
addRow( "<mi> x <mglyph fontfamily=\"testfont\" index=\"99\" alt=\"c\"/> d </mi>",
"<mi>x \n <mglyph fontfamily=\"testfont\" index=\"99\" alt=\"c\"/> d</mi>" );
addRow( "<mi> x y z </mi>",
"<mi>x y z</mi>" );
// Entities
addRow( "<mi>&CapitalDifferentialD;</mi>", true );
addRow( "<mi>&DifferentialD;</mi>", true );
}
void TestLoadAndSave::numberElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mn>1</mn>" );
addRow( "<mn>1.2</mn>" );
addRow( "<mn>1,2</mn>" );
addRow( "<mn>1 , 2</mn>" );
addRow( "<mn> 12 </mn>",
"<mn>12</mn>");
// Entities
addRow( "<mn>&ExponentialE;</mn>", true );
addRow( "<mn>&ImaginaryI;</mn>", true );
}
void TestLoadAndSave::operatorElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mo>+</mo>" );
// Check operator attributes. Section 3.2.5.2
addRow( "<mo form=\"prefix\">+</mo>" );
addRow( "<mo form=\"infix\">+</mo>" );
addRow( "<mo form=\"postfix\">+</mo>" );
addRow( "<mo fence=\"true\">+</mo>" );
addRow( "<mo fence=\"false\">+</mo>" );
addRow( "<mo separator=\"true\">+</mo>" );
addRow( "<mo separator=\"false\">+</mo>" );
addRow( "<mo lspace=\"10em\">+</mo>" );
addRow( "<mo lspace=\"10ex\">+</mo>" );
addRow( "<mo lspace=\"10px\">+</mo>" );
addRow( "<mo lspace=\"10in\">+</mo>" );
addRow( "<mo lspace=\"10cm\">+</mo>" );
addRow( "<mo lspace=\"10mm\">+</mo>" );
addRow( "<mo lspace=\"10pt\">+</mo>" );
addRow( "<mo lspace=\"10pc\">+</mo>" );
addRow( "<mo lspace=\"90%\">+</mo>" );
addRow( "<mo lspace=\"1.2\">+</mo>" );
addRow( "<mo lspace=\"veryverythinmathspace\">+</mo>" );
addRow( "<mo lspace=\"verythinmathspace\">+</mo>" );
addRow( "<mo lspace=\"thinmathspace\">+</mo>" );
addRow( "<mo lspace=\"mediummathspace\">+</mo>" );
addRow( "<mo lspace=\"thickmathspace\">+</mo>" );
addRow( "<mo lspace=\"verythickmathspace\">+</mo>" );
addRow( "<mo lspace=\"veryverythickmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativeveryverythinmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativeverythinmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativethinmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativemediummathspace\">+</mo>" );
addRow( "<mo lspace=\"negativethickmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativeverythickmathspace\">+</mo>" );
addRow( "<mo lspace=\"negativeveryverythickmathspace\">+</mo>" );
addRow( "<mo rspace=\"10em\">+</mo>" );
addRow( "<mo rspace=\"10ex\">+</mo>" );
addRow( "<mo rspace=\"10px\">+</mo>" );
addRow( "<mo rspace=\"10in\">+</mo>" );
addRow( "<mo rspace=\"10cm\">+</mo>" );
addRow( "<mo rspace=\"10mm\">+</mo>" );
addRow( "<mo rspace=\"10pt\">+</mo>" );
addRow( "<mo rspace=\"10pc\">+</mo>" );
addRow( "<mo rspace=\"90%\">+</mo>" );
addRow( "<mo rspace=\"1.2\">+</mo>" );
addRow( "<mo rspace=\"veryverythinmathspace\">+</mo>" );
addRow( "<mo rspace=\"verythinmathspace\">+</mo>" );
addRow( "<mo rspace=\"thinmathspace\">+</mo>" );
addRow( "<mo rspace=\"mediummathspace\">+</mo>" );
addRow( "<mo rspace=\"thickmathspace\">+</mo>" );
addRow( "<mo rspace=\"verythickmathspace\">+</mo>" );
addRow( "<mo rspace=\"veryverythickmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativeveryverythinmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativeverythinmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativethinmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativemediummathspace\">+</mo>" );
addRow( "<mo rspace=\"negativethickmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativeverythickmathspace\">+</mo>" );
addRow( "<mo rspace=\"negativeveryverythickmathspace\">+</mo>" );
addRow( "<mo stretchy=\"true\">+</mo>" );
addRow( "<mo stretchy=\"false\">+</mo>" );
addRow( "<mo symmetric=\"true\">+</mo>" );
addRow( "<mo symmetric=\"false\">+</mo>" );
addRow( "<mo maxsize=\"10em\">+</mo>" );
addRow( "<mo maxsize=\"10ex\">+</mo>" );
addRow( "<mo maxsize=\"10px\">+</mo>" );
addRow( "<mo maxsize=\"10in\">+</mo>" );
addRow( "<mo maxsize=\"10cm\">+</mo>" );
addRow( "<mo maxsize=\"10mm\">+</mo>" );
addRow( "<mo maxsize=\"10pt\">+</mo>" );
addRow( "<mo maxsize=\"10pc\">+</mo>" );
addRow( "<mo maxsize=\"90%\">+</mo>" );
addRow( "<mo maxsize=\"1.2\">+</mo>" );
addRow( "<mo maxsize=\"veryverythinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"verythinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"thinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"mediummathspace\">+</mo>" );
addRow( "<mo maxsize=\"thickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"verythickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"veryverythickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativeveryverythinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativeverythinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativethinmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativemediummathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativethickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativeverythickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"negativeveryverythickmathspace\">+</mo>" );
addRow( "<mo maxsize=\"infinity\">+</mo>" );
addRow( "<mo minsize=\"10em\">+</mo>" );
addRow( "<mo minsize=\"10ex\">+</mo>" );
addRow( "<mo minsize=\"10px\">+</mo>" );
addRow( "<mo minsize=\"10in\">+</mo>" );
addRow( "<mo minsize=\"10cm\">+</mo>" );
addRow( "<mo minsize=\"10mm\">+</mo>" );
addRow( "<mo minsize=\"10pt\">+</mo>" );
addRow( "<mo minsize=\"10pc\">+</mo>" );
addRow( "<mo minsize=\"90%\">+</mo>" );
addRow( "<mo minsize=\"1.2\">+</mo>" );
addRow( "<mo minsize=\"veryverythinmathspace\">+</mo>" );
addRow( "<mo minsize=\"verythinmathspace\">+</mo>" );
addRow( "<mo minsize=\"thinmathspace\">+</mo>" );
addRow( "<mo minsize=\"mediummathspace\">+</mo>" );
addRow( "<mo minsize=\"thickmathspace\">+</mo>" );
addRow( "<mo minsize=\"verythickmathspace\">+</mo>" );
addRow( "<mo minsize=\"veryverythickmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativeveryverythinmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativeverythinmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativethinmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativemediummathspace\">+</mo>" );
addRow( "<mo minsize=\"negativethickmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativeverythickmathspace\">+</mo>" );
addRow( "<mo minsize=\"negativeveryverythickmathspace\">+</mo>" );
addRow( "<mo largeop=\"true\">+</mo>" );
addRow( "<mo largeop=\"false\">+</mo>" );
addRow( "<mo movablelimits=\"true\">+</mo>" );
addRow( "<mo movablelimits=\"false\">+</mo>" );
addRow( "<mo accent=\"true\">+</mo>" );
addRow( "<mo accent=\"false\">+</mo>" );
// Entities
addRow( "<mo>&InvisibleTimes;</mo>", true );
addRow( "<mo>&InvisibleComma;</mo>", true );
addRow( "<mo>&ApplyFunction;</mo>", true );
}
void TestLoadAndSave::textElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mtext></mtext>" );
addRow( "<mtext>text</mtext>" );
addRow( "<mtext> text </mtext>",
"<mtext>text</mtext>");
addRow( "<mtext> Theorem 1: </mtext>",
"<mtext>Theorem 1:</mtext>" );
addRow( "<mtext> &ThinSpace; </mtext>",
"<mtext>&ThinSpace;</mtext>", true );
addRow( "<mtext> &ThickSpace;&ThickSpace; </mtext>",
"<mtext>&ThickSpace;&ThickSpace;</mtext>", true );
addRow( "<mtext> /* a comment */ </mtext>",
"<mtext>/* a comment */</mtext>" );
}
void TestLoadAndSave::spaceElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mspace/>" );
// Check operator attributes. Sefction 3.2.7.2
addRow( "<mspace width=\"10em\"/>" );
addRow( "<mspace width=\"10ex\"/>" );
addRow( "<mspace width=\"10px\"/>" );
addRow( "<mspace width=\"10in\"/>" );
addRow( "<mspace width=\"10cm\"/>" );
addRow( "<mspace width=\"10mm\"/>" );
addRow( "<mspace width=\"10pt\"/>" );
addRow( "<mspace width=\"10pc\"/>" );
addRow( "<mspace width=\"90%\"/>" );
addRow( "<mspace width=\"1.2\"/>" );
addRow( "<mspace width=\"veryverythinmathspace\"/>" );
addRow( "<mspace width=\"verythinmathspace\"/>" );
addRow( "<mspace width=\"thinmathspace\"/>" );
addRow( "<mspace width=\"mediummathspace\"/>" );
addRow( "<mspace width=\"thickmathspace\"/>" );
addRow( "<mspace width=\"verythickmathspace\"/>" );
addRow( "<mspace width=\"veryverythickmathspace\"/>" );
addRow( "<mspace width=\"negativeveryverythinmathspace\"/>" );
addRow( "<mspace width=\"negativeverythinmathspace\"/>" );
addRow( "<mspace width=\"negativethinmathspace\"/>" );
addRow( "<mspace width=\"negativemediummathspace\"/>" );
addRow( "<mspace width=\"negativethickmathspace\"/>" );
addRow( "<mspace width=\"negativeverythickmathspace\"/>" );
addRow( "<mspace width=\"negativeveryverythickmathspace\"/>" );
addRow( "<mspace height=\"10em\"/>" );
addRow( "<mspace height=\"10ex\"/>" );
addRow( "<mspace height=\"10px\"/>" );
addRow( "<mspace height=\"10in\"/>" );
addRow( "<mspace height=\"10cm\"/>" );
addRow( "<mspace height=\"10mm\"/>" );
addRow( "<mspace height=\"10pt\"/>" );
addRow( "<mspace height=\"10pc\"/>" );
addRow( "<mspace height=\"90%\"/>" );
addRow( "<mspace height=\"1.2\"/>" );
addRow( "<mspace depth=\"10em\"/>" );
addRow( "<mspace depth=\"10ex\"/>" );
addRow( "<mspace depth=\"10px\"/>" );
addRow( "<mspace depth=\"10in\"/>" );
addRow( "<mspace depth=\"10cm\"/>" );
addRow( "<mspace depth=\"10mm\"/>" );
addRow( "<mspace depth=\"10pt\"/>" );
addRow( "<mspace depth=\"10pc\"/>" );
addRow( "<mspace depth=\"90%\"/>" );
addRow( "<mspace depth=\"1.2\"/>" );
addRow( "<mspace linebreak=\"auto\"/>" );
addRow( "<mspace linebreak=\"newline\"/>" );
addRow( "<mspace linebreak=\"indentingnewline\"/>" );
addRow( "<mspace linebreak=\"nobreak\"/>" );
addRow( "<mspace linebreak=\"goodbreak\"/>" );
addRow( "<mspace linebreak=\"badbreak\"/>" );
}
void TestLoadAndSave::stringElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<ms>text</ms>",
"<ms>text</ms>" );
addRow( "<ms> more text </ms>",
"<ms>more text</ms>" );
// Glyph element contents
//addRow( "<ms> foo </ms>"); // marker just to have a failing test.
#if 0 // FIXME: These tests make the test program crash. Investigate and fix.
addRow( "<ms>tex<mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/></ms>",
"<ms>tex<mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/></ms>");
addRow( "<ms> <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>",
"<ms> <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>" );
addRow( "<ms>te <mglyph fontfamily=\"serif\" alt=\"x\" index=\"120\"/> "
" <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>",
"<ms>te <mglyph fontfamily=\"serif\" alt=\"x\" index=\"120\"/> "
" <mglyph fontfamily=\"serif\" alt=\"t\" index=\"116\"/> </ms>" );
#endif
// Attributes
addRow( "<ms mathvariant=\"bold\">text</ms>",
"<ms mathvariant=\"bold\">text</ms>" );
addRow( "<ms fontsize=\"18pt\">text</ms>",
"<ms fontsize=\"18pt\">text</ms>" );
// Entities
addRow( "<ms> &amp; </ms>",
"<ms>&amp;</ms>" );
addRow( "<ms> &amp;amp; </ms>",
"<ms>&amp;amp;</ms>" );
}
void TestLoadAndSave::glyphElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Glyph element does not have content
addRow( "<mglyph fontfamily=\"serif\" index=\"97\" alt=\"a\"/>" );
}
void TestLoadAndSave::mathVariant_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of mathvariant attributes
*/
addRow( "<mi mathvariant=\"normal\">x</mi>" );
addRow( "<mi mathvariant=\"bold\">x</mi>" );
addRow( "<mi mathvariant=\"italic\">x</mi>" );
addRow( "<mi mathvariant=\"bold-italic\">x</mi>" );
addRow( "<mi mathvariant=\"double-struck\">x</mi>" );
addRow( "<mi mathvariant=\"bold-fraktur\">x</mi>" );
addRow( "<mi mathvariant=\"fraktur\">x</mi>" );
addRow( "<mi mathvariant=\"sans-serif\">x</mi>" );
addRow( "<mi mathvariant=\"bold-sans-serif\">x</mi>" );
addRow( "<mi mathvariant=\"sans-serif-italic\">x</mi>" );
addRow( "<mi mathvariant=\"sans-serif-bold-italic\">x</mi>" );
addRow( "<mi mathvariant=\"monospace\">x</mi>" );
/*
* Unallowed mathvariant values should be removed
*/
addRow( "<mi mathvariant=\"invalid\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi mathvariant=\"Bold\">x</mi>",
"<mi mathvariant=\"bold\">x</mi>" );
addRow( "<mi mathvariant=\"BOLD\">x</mi>",
"<mi mathvariant=\"bold\">x</mi>" );
addRow( "<mi MATHVARIANT=\"bold\">x</mi>",
"<mi mathvariant=\"bold\">x</mi>" );
addRow( "<mi MathVariant=\"bold\">x</mi>",
"<mi mathvariant=\"bold\">x</mi>" );
}
void TestLoadAndSave::mathSize_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of mathsize attributes
*/
addRow( "<mi mathsize=\"small\">x</mi>" );
addRow( "<mi mathsize=\"normal\">x</mi>" );
addRow( "<mi mathsize=\"big\">x</mi>" );
addRow( "<mi mathsize=\"10em\">x</mi>" );
addRow( "<mi mathsize=\"10ex\">x</mi>" );
addRow( "<mi mathsize=\"10px\">x</mi>" );
addRow( "<mi mathsize=\"10in\">x</mi>" );
addRow( "<mi mathsize=\"10cm\">x</mi>" );
addRow( "<mi mathsize=\"10mm\">x</mi>" );
addRow( "<mi mathsize=\"10pt\">x</mi>" );
addRow( "<mi mathsize=\"10pc\">x</mi>" );
addRow( "<mi mathsize=\"90%\">x</mi>" );
addRow( "<mi mathsize=\"1.2\">x</mi>" );
/*
* Unallowed mathsize values should be removed
*/
addRow( "<mi mathsize=\"invalid\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi mathsize=\"Normal\">x</mi>",
"<mi mathsize=\"normal\">x</mi>" );
addRow( "<mi mathsize=\"NORMAL\">x</mi>",
"<mi mathsize=\"normal\">x</mi>");
addRow( "<mi MATHSIZE=\"normal\">x</mi>",
"<mi mathsize=\"normal\">x</mi>" );
addRow( "<mi MathSize=\"normal\">x</mi>",
"<mi mathsize=\"normal\">x</mi>" );
}
void TestLoadAndSave::mathColor_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of mathcolor attributes
*/
addRow( "<mi mathcolor=\"white\">x</mi>" );
addRow( "<mi mathcolor=\"black\">x</mi>" );
addRow( "<mi mathcolor=\"green\">x</mi>" );
addRow( "<mi mathcolor=\"#abc\">x</mi>" );
addRow( "<mi mathcolor=\"#abcdef\">x</mi>" );
/*
* Unallowed mathcolor values should be removed
*/
addRow( "<mi mathcolor=\"invalid\">x</mi>",
"<mi>x</mi>", true );
addRow( "<mi mathcolor=\"#abcdefg\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone and performance consuming.
*/
addRow( "<mi mathcolor=\"Black\">x</mi>",
"<mi mathcolor=\"black\">x</mi>" );
addRow( "<mi mathcolor=\"BLACK\">x</mi>",
"<mi mathcolor=\"black\">x</mi>");
addRow( "<mi MATHCOLOR=\"black\">x</mi>",
"<mi mathcolor=\"black\">x</mi>" );
addRow( "<mi MathColor=\"black\">x</mi>",
"<mi mathcolor=\"black\">x</mi>" );
addRow( "<mi MathColor=\"#ABC\">x</mi>",
"<mi mathcolor=\"#abc\">x</mi>" );
}
void TestLoadAndSave::mathBackground_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of mathbackground attributes
*/
addRow( "<mi mathbackground=\"white\">x</mi>" );
addRow( "<mi mathbackground=\"black\">x</mi>" );
addRow( "<mi mathbackground=\"green\">x</mi>" );
addRow( "<mi mathbackground=\"#abc\">x</mi>" );
addRow( "<mi mathbackground=\"#abcdef\">x</mi>" );
/*
* Unallowed mathbackground values should be removed
*/
addRow( "<mi mathbackground=\"invalid\">x</mi>",
"<mi>x</mi>", true );
addRow( "<mi mathbackground=\"#abcdefg\">x</mi>",
"<mi>x</mi>", true);
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi mathbackground=\"Black\">x</mi>",
"<mi mathbackground=\"black\">x</mi>" );
addRow( "<mi mathbackground=\"BLACK\">x</mi>",
"<mi mathbackground=\"black\">x</mi>");
addRow( "<mi MATHBACKGROUND=\"black\">x</mi>",
"<mi mathbackground=\"black\">x</mi>" );
addRow( "<mi MathBackground=\"black\">x</mi>",
"<mi mathbackground=\"black\">x</mi>" );
addRow( "<mi MathBackground=\"#ABC\">x</mi>",
"<mi mathbackground=\"#abc\">x</mi>" );
}
void TestLoadAndSave::fontSize_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of fontsize attributes
*/
addRow( "<mi fontsize=\"10em\">x</mi>" );
addRow( "<mi fontsize=\"10ex\">x</mi>" );
addRow( "<mi fontsize=\"10px\">x</mi>" );
addRow( "<mi fontsize=\"10in\">x</mi>" );
addRow( "<mi fontsize=\"10cm\">x</mi>" );
addRow( "<mi fontsize=\"10mm\">x</mi>" );
addRow( "<mi fontsize=\"10pt\">x</mi>" );
addRow( "<mi fontsize=\"10pc\">x</mi>" );
addRow( "<mi fontsize=\"90%\">x</mi>" );
addRow( "<mi fontsize=\"1.2\">x</mi>" );
/*
* Unallowed fontsize values should be removed
*/
addRow( "<mi fontsize=\"invalid\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi fontsize=\"10Em\">x</mi>",
"<mi fontsize=\"10em\">x</mi>" );
addRow( "<mi fontsize=\"10EM\">x</mi>",
"<mi fontsize=\"10em\">x</mi>");
addRow( "<mi FONTSIZE=\"10em\">x</mi>",
"<mi fontsize=\"10em\">x</mi>" );
addRow( "<mi FontSize=\"10em\">x</mi>",
"<mi fontsize=\"10em\">x</mi>" );
}
void TestLoadAndSave::fontWeight_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of fontweight attributes
*/
addRow( "<mi fontweight=\"bold\">x</mi>" );
addRow( "<mi fontweight=\"normal\">x</mi>" );
/*
* Unallowed fontweight values should be removed
*/
addRow( "<mi fontweight=\"invalid\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi fontweight=\"Bold\">x</mi>",
"<mi fontweight=\"bold\">x</mi>" );
addRow( "<mi fontweight=\"BOLD\">x</mi>",
"<mi fontweight=\"bold\">x</mi>");
addRow( "<mi FONTWEIGHT=\"bold\">x</mi>",
"<mi fontweight=\"bold\">x</mi>" );
addRow( "<mi FontWeight=\"bold\">x</mi>",
"<mi fontweight=\"bold\">x</mi>" );
}
void TestLoadAndSave::fontStyle_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of fontstyle attributes
*/
addRow( "<mi fontstyle=\"italic\">x</mi>" );
addRow( "<mi fontstyle=\"normal\">x</mi>" );
/*
* Unallowed fontstyle values should be removed
*/
addRow( "<mi fontstyle=\"invalid\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi fontstyle=\"Italic\">x</mi>",
"<mi fontstyle=\"italic\">x</mi>" );
addRow( "<mi fontstyle=\"ITALIC\">x</mi>",
"<mi fontstyle=\"italic\">x</mi>");
addRow( "<mi FONTSTYLE=\"italic\">x</mi>",
"<mi fontstyle=\"italic\">x</mi>" );
addRow( "<mi FontStyle=\"italic\">x</mi>",
"<mi fontstyle=\"italic\">x</mi>" );
}
void TestLoadAndSave::color_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
/*
* Test all possible values of color attributes
*/
addRow( "<mi color=\"white\">x</mi>" );
addRow( "<mi color=\"black\">x</mi>" );
addRow( "<mi color=\"green\">x</mi>" );
addRow( "<mi color=\"#abc\">x</mi>" );
addRow( "<mi color=\"#abcdef\">x</mi>" );
/*
* Unallowed color values should be removed
*/
addRow( "<mi color=\"invalid\">x</mi>",
"<mi>x</mi>", true );
addRow( "<mi color=\"#abcdefg\">x</mi>",
"<mi>x</mi>", true );
/*
* It's better to store attribute names and values lowercase and avoid
* having to check whether it's upper or lower case on a per-use case,
* which is more error prone performance consuming.
*/
addRow( "<mi color=\"Black\">x</mi>",
"<mi color=\"black\">x</mi>" );
addRow( "<mi color=\"BLACK\">x</mi>",
"<mi color=\"black\">x</mi>");
addRow( "<mi COLOR=\"black\">x</mi>",
"<mi color=\"black\">x</mi>" );
addRow( "<mi Color=\"black\">x</mi>",
"<mi color=\"black\">x</mi>" );
addRow( "<mi Color=\"#ABC\">x</mi>",
"<mi color=\"#abc\">x</mi>" );
}
void TestLoadAndSave::rowElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mrow></mrow>",
"<mrow/>" );
addRow( "<mrow><mi>x</mi></mrow>", // Collapse mrow with only one child to the child only.
"<mi>x</mi>" );
addRow( "<mrow><mi>x</mi><mi>y</mi></mrow>",
"<mrow>\n <mi>x</mi>\n <mi>y</mi>\n</mrow>" );
// More complex content
addRow( "<mrow><mrow></mrow></mrow>", // Collapse row with no children to nothing
"<mrow/>");
addRow( "<mrow><mrow><mi>x</mi></mrow></mrow>",
//"<mrow>\n <mi>x</mi>\n</mrow>");
"<mi>x</mi>");
addRow( "<mrow><mrow><mi>x</mi><mn>2</mn></mrow></mrow>",
"<mrow>\n <mi>x</mi>\n <mn>2</mn>\n</mrow>");
addRow( "<mrow><mrow><mi>x</mi><mn>2</mn></mrow><mi>y</mi></mrow>",
"<mrow>\n <mrow>\n <mi>x</mi>\n <mn>2</mn>\n </mrow>\n <mi>y</mi>\n</mrow>");
#if 0 // Enable this when entities work (see &InvisibleTimes; below).
addRow( "<mrow>"
" <mrow>"
" <mn> 2 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <mi> x </mi>"
" </mrow>"
" <mo> + </mo>"
" <mi> y </mi>"
" <mo> - </mo>"
" <mi> z </mi>"
"</mrow>",
"<mrow>\n"
" <mrow>\n"
" <mn>2</mn>\n"
" <mo>&InvisibleTimes;</mo>\n"
" <mi>x</mi>\n"
" </mrow>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" <mo>-</mo>\n"
" <mi>z</mi>\n"
"</mrow>" );
#endif
}
void TestLoadAndSave::fractionElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mfrac><mi>x</mi><mi>y</mi></mfrac>",
"<mfrac>\n"
" <mi>x</mi>\n"
" <mi>y</mi>\n"
"</mfrac>");
}
void TestLoadAndSave::rootElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
addRow( "<mroot><mi>x</mi><mn>2</mn></mroot>",
"<mroot>\n <mi>x</mi>\n <mn>2</mn>\n</mroot>");
}
void TestLoadAndSave::styleElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mstyle></mstyle>",
"<mstyle/>" );
addRow( "<mstyle><mrow></mrow></mstyle>",
"<mstyle/>");
addRow( "<mstyle>\n <mi>x</mi>\n</mstyle>" );
addRow( "<mstyle><mrow><mi>x</mi></mrow></mstyle>",
"<mstyle>\n <mi>x</mi>\n</mstyle>");
// Be sure attributes don't break anything
addRow( "<mstyle mathvariant=\"bold\">\n <mi>x</mi>\n</mstyle>" );
addRow( "<mstyle thinmathspace=\"0.5em\">\n <mi>x</mi>\n</mstyle>" );
}
void TestLoadAndSave::errorElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<merror></merror>",
"<merror/>");
addRow( "<merror><mrow></mrow></merror>",
"<merror/>" );
addRow( "<merror>\n <mi>x</mi>\n</merror>" );
addRow( "<merror><mrow><mi>x</mi></mrow></merror>",
"<merror>\n <mi>x</mi>\n</merror>" );
// More complex content
addRow( "<merror>\n"
" <mtext>Unrecognized element: mfraction; arguments were:</mtext>\n"
" <mrow>\n <mn>1</mn>\n <mo>+</mo>\n <msqrt>\n <mn>5</mn>\n </msqrt>\n </mrow>\n"
" <mtext>and</mtext>\n"
" <mn>2</mn>\n"
"</merror>" );
}
void TestLoadAndSave::paddedElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mpadded></mpadded>",
"<mpadded/>" );
addRow( "<mpadded><mrow></mrow></mpadded>",
"<mpadded/>" );
addRow( "<mpadded>\n <mi>x</mi>\n</mpadded>" );
addRow( "<mpadded><mrow><mi>x</mi></mrow></mpadded>",
"<mpadded>\n <mi>x</mi>\n</mpadded>" );
// Be sure attributes don't break anything
addRow( "<mpadded width=\"+0.8em\">\n <mi>x</mi>\n</mpadded>" );
addRow( "<mpadded depth=\"1.2\">\n <mi>x</mi>\n</mpadded>" );
}
void TestLoadAndSave::phantomElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mphantom></mphantom>",
"<mphantom/>" );
addRow( "<mphantom><mrow></mrow></mphantom>",
"<mphantom/>" );
addRow( "<mphantom>\n <mi>x</mi>\n</mphantom>" );
addRow( "<mphantom><mrow><mi>x</mi></mrow></mphantom>",
"<mphantom>\n <mi>x</mi>\n</mphantom>" );
// Attributes
addRow( "<mphantom width=\"+0.8em\">\n <mi>x</mi>\n</mphantom>" );
addRow( "<mphantom depth=\"1.2\">\n <mi>x</mi>\n</mphantom>" );
}
void TestLoadAndSave::fencedElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// This is an inferred mrow element
addRow( "<mfenced></mfenced>",
"<mfenced/>" );
addRow( "<mfenced>\n <mi>x</mi>\n</mfenced>" );
addRow( "<mfenced>\n <mi>x</mi>\n <mn>2</mn>\n</mfenced>" );
}
void TestLoadAndSave::encloseElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<menclose></menclose>",
"<menclose/>" );
addRow( "<menclose><mrow></mrow></menclose>",
"<menclose/>");
addRow( "<menclose>\n <mi>x</mi>\n</menclose>" );
addRow( "<menclose><mrow><mi>x</mi></mrow></menclose>",
"<menclose>\n <mi>x</mi>\n</menclose>");
// Attributes
addRow( "<menclose notation=\"longdiv\">\n <mi>x</mi>\n</menclose>" );
addRow( "<menclose notation=\"downdiagonalstrike\">\n <mi>x</mi>\n</menclose>" );
}
void TestLoadAndSave::subElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<msub>\n <mrow></mrow>\n <mrow></mrow>\n</msub>",
"<msub>\n <mrow/>\n <mrow/>\n</msub>");
addRow( "<msub>\n <mi>x</mi>\n <mi>y</mi>\n</msub>" );
addRow( "<msub>\n <mi>x</mi>\n <mi>y</mi>\n</msub>" );
addRow( "<msub>\n <mi>x</mi>\n <mi>y</mi>\n</msub>" );
// More complex content
addRow( "<msub>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mn>2</mn>\n"
"</msub>" );
// Attributes
addRow( "<msub subscriptshift=\"1.5ex\">\n <mi>x</mi>\n <mi>y</mi>\n</msub>" );
addRow( "<msub subscriptshift=\"1.5\">\n <mi>x</mi>\n <mi>y</mi>\n</msub>" );
}
void TestLoadAndSave::supElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<msup>\n <mrow></mrow>\n <mrow></mrow>\n</msup>",
"<msup>\n <mrow/>\n <mrow/>\n</msup>");
addRow( "<msup>\n <mi>x</mi>\n <mi>y</mi>\n</msup>" );
addRow( "<msup>\n <mi>x</mi>\n <mi>y</mi>\n</msup>" );
addRow( "<msup>\n <mi>x</mi>\n <mi>y</mi>\n</msup>" );
// More complex content
addRow( "<msup>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mn>2</mn>\n"
"</msup>" );
// Attributes
addRow( "<msup subscriptshift=\"1.5ex\">\n <mi>x</mi>\n <mi>y</mi>\n</msup>" );
addRow( "<msup subscriptshift=\"1.5\">\n <mi>x</mi>\n <mi>y</mi>\n</msup>" );
}
void TestLoadAndSave::subsupElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<msubsup><mrow></mrow><mrow></mrow><mrow></mrow></msubsup>",
"<msubsup>\n <mrow/>\n <mrow/>\n <mrow/>\n</msubsup>");
addRow( "<msubsup>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</msubsup>" );
addRow( "<msubsup><mrow><mi>x</mi></mrow><mi>y</mi><mi>z</mi></msubsup>",
"<msubsup>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</msubsup>");
addRow( "<msubsup><mi>x</mi><mi>y</mi><mrow><mi>z</mi></mrow></msubsup>",
"<msubsup>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</msubsup>");
addRow( "<msubsup>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mi>i</mi>\n"
" <mn>2</mn>\n"
"</msubsup>" );
// Attributes
addRow( "<msubsup subscriptshift=\"1.5ex\">\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</msubsup>" );
addRow( "<msubsup superscriptshift=\"1.5ex\">\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</msubsup>" );
}
void TestLoadAndSave::underElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<munder>\n <mrow></mrow>\n <mrow></mrow>\n</munder>",
"<munder>\n <mrow/>\n <mrow/>\n</munder>");
addRow( "<munder>\n <mi>x</mi>\n <mi>y</mi>\n</munder>" );
addRow( "<munder>\n <mi>x</mi>\n <mi>y</mi>\n</munder>" );
addRow( "<munder>\n <mi>x</mi>\n <mi>y</mi>\n</munder>" );
// More complex content
addRow( "<munder>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mn>2</mn>\n"
"</munder>" );
// Attributes
addRow( "<munder accentunder=\"true\">\n <mi>x</mi>\n <mi>y</mi>\n</munder>" );
}
void TestLoadAndSave::overElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mover>\n <mrow></mrow>\n <mrow></mrow>\n</mover>",
"<mover>\n <mrow/>\n <mrow/>\n</mover>");
addRow( "<mover>\n <mi>x</mi>\n <mi>y</mi>\n</mover>" );
addRow( "<mover>\n <mi>x</mi>\n <mi>y</mi>\n</mover>" );
addRow( "<mover>\n <mi>x</mi>\n <mi>y</mi>\n</mover>" );
// More complex content
addRow( "<mover>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mn>2</mn>\n"
"</mover>" );
// Attributes
addRow( "<mover accent=\"true\">\n <mi>x</mi>\n <mi>y</mi>\n</mover>" );
}
void TestLoadAndSave::underoverElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<munderover><mrow></mrow><mrow></mrow><mrow></mrow></munderover>",
"<munderover>\n <mrow/>\n <mrow/>\n <mrow/>\n</munderover>");
addRow( "<munderover>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</munderover>" );
addRow( "<munderover><mrow><mi>x</mi></mrow><mi>y</mi><mi>z</mi></munderover>",
"<munderover>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</munderover>");
addRow( "<munderover><mi>x</mi><mi>y</mi><mrow><mi>z</mi></mrow></munderover>",
"<munderover>\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</munderover>");
addRow( "<munderover>\n"
" <mrow>\n"
" <mo>(</mo>\n"
" <mrow>\n"
" <mi>x</mi>\n"
" <mo>+</mo>\n"
" <mi>y</mi>\n"
" </mrow>\n"
" <mo>)</mo>\n"
" </mrow>\n"
" <mi>i</mi>\n"
" <mn>2</mn>\n"
"</munderover>" );
// Attributes
addRow( "<munderover accent=\"true\">\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</munderover>" );
addRow( "<munderover accentunder=\"true\">\n <mi>x</mi>\n <mi>y</mi>\n <mi>z</mi>\n</munderover>" );
}
void TestLoadAndSave::multiscriptsElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mmultiscripts><mi>x</mi><mi>i</mi><mi>j</mi></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <mi>i</mi>\n <mi>j</mi>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><mprescripts/><mi>i</mi><mi>j</mi></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <mprescripts/>\n <mi>i</mi>\n <mi>j</mi>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><mi>i</mi><none/></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <mi>i</mi>\n <none/>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><none/><none/></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <none/>\n <none/>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><mprescripts/><none/><none/></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <mprescripts/>\n <none/>\n <none/>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><none/><none/><mprescripts/><none/><none/></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <none/>\n <none/>\n <mprescripts/>\n <none/>\n <none/>\n</mmultiscripts>" );
addRow( "<mmultiscripts><mi>x</mi><mi>x</mi><none/><mprescripts/><mi>y</mi><none/></mmultiscripts>",
"<mmultiscripts>\n <mi>x</mi>\n <mi>x</mi>\n <none/>\n <mprescripts/>\n <mi>y</mi>\n <none/>\n</mmultiscripts>" );
// More complex content
addRow( "<mmultiscripts>\n"
" <mi>R</mi>\n"
" <mi>i</mi>\n"
" <none/>\n"
" <none/>\n"
" <mi>j</mi>\n"
" <mi>k</mi>\n"
" <none/>\n"
" <mi>l</mi>\n"
" <none/>\n"
"</mmultiscripts>" );
}
void TestLoadAndSave::tableElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mtable></mtable>",
"<mtable/>" );
addRow( "<mtable><mtr></mtr></mtable>",
"<mtable>\n <mtr/>\n</mtable>" );
addRow( "<mtable><mtr><mtd></mtd></mtr></mtable>",
"<mtable>\n <mtr>\n <mtd/>\n </mtr>\n</mtable>" );
addRow( "<mtable><mtr><mtd><mrow></mrow></mtd></mtr></mtable>",
"<mtable>\n <mtr>\n <mtd/>\n </mtr>\n</mtable>" ); // mtd is an inferred mrow
addRow( "<mtable><mtr><mtd><mrow><mi>x</mi></mrow></mtd></mtr></mtable>",
"<mtable>\n <mtr>\n <mtd>\n <mi>x</mi>\n </mtd>\n </mtr>\n</mtable>" );
// addRow( "<mtable><mlabeledtr><mrow></mrow></mlabeledtr></mtable>",
// "<mtable><mlabeledtr><mrow></mrow></mlabeledtr></mtable>" );
// addRow( "<mtable><mlabeledtr><mrow></mrow><mtd></mtd></mlabeledtr></mtable>",
// "<mtable><mlabeledtr><mrow></mrow><mtd></mtd></mlabeledtr></mtable>" );
// More complex content (unity matrix)
addRow( "<mtable>\n"
" <mtr>\n"
" <mtd>\n <mn>1</mn>\n </mtd>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" </mtr>\n"
" <mtr>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" <mtd>\n <mn>1</mn>\n </mtd>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" </mtr>\n"
" <mtr>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" <mtd>\n <mn>0</mn>\n </mtd>\n"
" <mtd>\n <mn>1</mn>\n </mtd>\n"
" </mtr>\n"
"</mtable>" );
// Attributes
addRow( "<mtable align=\"top\">\n <mtr>\n <mtd>\n <mi>x</mi>\n </mtd>\n </mtr>\n</mtable>" );
addRow( "<mtable rowalign=\"center\">\n <mtr>\n <mtd>\n <mi>x</mi>\n </mtd>\n </mtr>\n</mtable>" );
// Content with alignment elements
/* addRow( "<mtable groupalign=\"{decimalpoint left left decimalpoint left left decimalpoint}\">"
" <mtr>"
" <mtd>"
" <mrow>"
" <mrow>"
" <mrow>"
" <maligngroup/>"
" <mn> 8.44 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> x </mi>"
" </mrow>"
" <maligngroup/>"
" <mo> + </mo>"
" <mrow>"
" <maligngroup/>"
" <mn> 55 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> y </mi>"
" </mrow>"
" </mrow>"
" <maligngroup/>"
" <mo> = </mo>"
" <maligngroup/>"
" <mn> 0 </mn>"
" </mrow>"
" </mtd>"
" </mtr>"
" <mtr>"
" <mtd>"
" <mrow>"
" <mrow>"
" <mrow>"
" <maligngroup/>"
" <mn> 3.1 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> x </mi>"
" </mrow>"
" <maligngroup/>"
" <mo> - </mo>"
" <mrow>"
" <maligngroup/>"
" <mn> 0.7 </mn>"
" <mo> &InvisibleTimes; </mo>"
" <maligngroup/>"
" <mi> y </mi>"
" </mrow>"
" </mrow>"
" <maligngroup/>"
" <mo> = </mo>"
" <maligngroup/>"
" <mrow>"
" <mo> - </mo>"
" <mn> 1.1 </mn>"
" </mrow>"
" </mrow>"
" </mtd>"
" </mtr>"
"</mtable>" );*/
}
void TestLoadAndSave::trElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mtr></mtr>",
"<mtr/>" );
addRow( "<mtr><mtd></mtd></mtr>",
"<mtr>\n <mtd/>\n</mtr>" );
addRow( "<mtr><mtd><mrow></mrow></mtd></mtr>",
"<mtr>\n <mtd/>\n</mtr>" ); // <mtd> is an inferred <mrow>
addRow( "<mtr><mtd><mi>x</mi></mtd></mtr>",
"<mtr>\n <mtd>\n <mi>x</mi>\n </mtd>\n</mtr>" );
addRow( "<mtr><mtd><mrow><mi>x</mi></mrow></mtd></mtr>",
"<mtr>\n <mtd>\n <mi>x</mi>\n </mtd>\n</mtr>" );
// More complex content
addRow( "<mtr id=\"e-is-m-c-square\">\n"
" <mtd>\n"
" <mrow>\n"
" <mi>E</mi>\n"
" <mo>=</mo>\n"
" <mrow>\n"
" <mi>m</mi>\n"
//" <mo>&it;</mo>\n" FIXME: When entities work again, switch the next line for this one.
" <mo>*</mo>\n"
" <msup>\n"
" <mi>c</mi>\n"
" <mn>2</mn>\n"
" </msup>\n"
" </mrow>\n"
" </mrow>\n"
" </mtd>\n"
" <mtd>\n"
" <mtext>(2.1)</mtext>\n"
" </mtd>\n"
"</mtr>" );
// Be sure attributes don't break anything
addRow( "<mtr rowalign=\"top\"><mtd><mi>x</mi></mtd></mtr>",
"<mtr rowalign=\"top\">\n <mtd>\n <mi>x</mi>\n </mtd>\n</mtr>" );
addRow( "<mtr groupalign=\"left\"><mtd><mi>x</mi></mtd></mtr>",
"<mtr groupalign=\"left\">\n <mtd>\n <mi>x</mi>\n </mtd>\n</mtr>" );
}
// labeledtr is not yet implemented
#if 0
void TestLoadAndSave::labeledtrElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// TODO
//addRow( "<labeledtr/>" );
}
#endif
void TestLoadAndSave::tdElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<mtd></mtd>",
"<mtd/>" );
addRow( "<mtd/><mrow></mrow></mtd>",
"<mtd/>" );
addRow( "<mtd>\n <mi>x</mi>\n</mtd>" );
addRow( "<mtd><mrow><mi>x</mi></mrow></mtd>", // mrow with one element is deleted
"<mtd>\n <mi>x</mi>\n</mtd>");
// Be sure attributes don't break anything
addRow( "<mtd rowspan=\"3\">\n <mi>x</mi>\n</mtd>" );
addRow( "<mtd groupalign=\"left\">\n <mi>x</mi>\n</mtd>" );
}
void TestLoadAndSave::actionElement_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
QTest::addColumn<bool>("expectedFail");
// Basic content
addRow( "<maction actiontype=\"toggle\" selection=\"positive-integer\"><mrow></mrow><mrow></mrow></maction>",
"<maction actiontype=\"toggle\" selection=\"positive-integer\"/>" ); //
addRow( "<maction actiontype=\"statusline\"><mrow></mrow><mrow></mrow></maction>",
"<maction actiontype=\"statusline\"/>" );
addRow( "<maction actiontype=\"tooltip\"><mrow></mrow><mrow></mrow></maction>",
"<maction actiontype=\"tooltip\"/>" );
// This is expected to fail since we are only comparing attributes in a certain order.
// In reality it works, but we have to improve the test.
addRow( "<maction actiontype=\"highlight\" my:color=\"red\" my:background=\"yellow\"><mrow></mrow></maction>",
"<maction actiontype=\"highlight\" my:color=\"red\" my:background=\"yellow\"/>",
true );
}
void TestLoadAndSave::identifierElement()
{
test( new IdentifierElement );
}
void TestLoadAndSave::numberElement()
{
test( new NumberElement );
}
void TestLoadAndSave::operatorElement()
{
test( new OperatorElement );
}
void TestLoadAndSave::textElement()
{
test( new TextElement );
}
void TestLoadAndSave::spaceElement()
{
test( new SpaceElement );
}
void TestLoadAndSave::stringElement()
{
test( new StringElement );
}
void TestLoadAndSave::glyphElement()
{
test( new GlyphElement );
}
void TestLoadAndSave::mathVariant()
{
identifierElement();
}
void TestLoadAndSave::mathSize()
{
identifierElement();
}
void TestLoadAndSave::mathColor()
{
identifierElement();
}
void TestLoadAndSave::mathBackground()
{
identifierElement();
}
void TestLoadAndSave::fontSize()
{
identifierElement();
}
void TestLoadAndSave::fontWeight()
{
identifierElement();
}
void TestLoadAndSave::fontStyle()
{
identifierElement();
}
void TestLoadAndSave::color()
{
identifierElement();
}
void TestLoadAndSave::rowElement()
{
test( new RowElement );
}
void TestLoadAndSave::fractionElement()
{
test( new FractionElement );
}
void TestLoadAndSave::rootElement()
{
test( new RootElement );
}
void TestLoadAndSave::styleElement()
{
test( new StyleElement );
}
void TestLoadAndSave::errorElement()
{
test( new ErrorElement );
}
void TestLoadAndSave::paddedElement()
{
test( new PaddedElement );
}
void TestLoadAndSave::phantomElement()
{
test( new PhantomElement );
}
void TestLoadAndSave::fencedElement()
{
test( new FencedElement );
}
void TestLoadAndSave::encloseElement()
{
test( new EncloseElement );
}
void TestLoadAndSave::subElement()
{
test( new SubSupElement(0, SubScript) );
}
void TestLoadAndSave::supElement()
{
test( new SubSupElement(0, SupScript) );
}
void TestLoadAndSave::subsupElement()
{
test( new SubSupElement(0, SubSupScript) );
}
void TestLoadAndSave::underElement()
{
test( new UnderOverElement(0, Under) );
}
void TestLoadAndSave::overElement()
{
test( new UnderOverElement(0, Over) );
}
void TestLoadAndSave::underoverElement()
{
test( new UnderOverElement(0, UnderOver) );
}
void TestLoadAndSave::multiscriptsElement()
{
test( new MultiscriptElement );
}
void TestLoadAndSave::tableElement()
{
test( new TableElement );
}
void TestLoadAndSave::trElement()
{
test( new TableRowElement );
}
#if 0 // NYI
void TestLoadAndSave::labeledtrElement()
{
test( new TableRowElement );
}
#endif
void TestLoadAndSave::tdElement()
{
test( new TableDataElement );
}
void TestLoadAndSave::actionElement()
{
test( new ActionElement );
}
QTEST_MAIN(TestLoadAndSave)
diff --git a/plugins/musicshape/core/tests/MusicXmlWriterTest.cpp b/plugins/musicshape/core/tests/MusicXmlWriterTest.cpp
index 1709999e8c7..da4f68092a8 100644
--- a/plugins/musicshape/core/tests/MusicXmlWriterTest.cpp
+++ b/plugins/musicshape/core/tests/MusicXmlWriterTest.cpp
@@ -1,275 +1,275 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "Sheet.h"
#include "Part.h"
#include "PartGroup.h"
#include "MusicXmlWriter.h"
#include "VoiceBar.h"
#include "Bar.h"
#include "Chord.h"
#include "Note.h"
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
-#include <QtTest>
+#include <QTest>
#include <QBuffer>
using namespace MusicCore;
bool compareNodes(KoXmlNode& valid, KoXmlNode& result, QString path = QString());
bool validateOutput(MusicCore::Sheet* sheet, const char* fname);
class MusicXmlWriterTest : public QObject
{
Q_OBJECT
private slots:
void init()
{
}
void cleanup()
{
}
void testParts()
{
Sheet* sheet = new Sheet();
Part* p = sheet->addPart("first part");
p->setShortName("part1");
sheet->addPart("second part");
sheet->addBar();
validateOutput(sheet, "parts.xml");
delete sheet;
}
void testPartGroups()
{
Sheet* sheet = new Sheet();
sheet->addBar();
for (int i = 0; i < 8; i++) {
sheet->addPart(QString("part %1").arg(i));
}
PartGroup* pg = sheet->addPartGroup(0, 1);
pg->setName("group 1");
pg = sheet->addPartGroup(2, 3);
pg->setSymbol(PartGroup::Brace);
pg = sheet->addPartGroup(4, 5);
pg->setSymbol(PartGroup::Line);
pg = sheet->addPartGroup(6, 7);
pg->setSymbol(PartGroup::Bracket);
pg->setCommonBarLines(false);
validateOutput(sheet, "partgroups.xml");
delete sheet;
}
void testNestedPartGroups()
{
Sheet* sheet = new Sheet();
sheet->addBar();
for (int i = 0; i < 7; i++) {
sheet->addPart(QString("part %1").arg(i));
}
sheet->addPartGroup(0, 1)->setName("group 1");
sheet->addPartGroup(1, 2)->setName("group 2");
sheet->addPartGroup(2, 3)->setName("group 3");
sheet->addPartGroup(0, 6)->setName("group 4");
sheet->addPartGroup(4, 5)->setName("group 5");
sheet->addPartGroup(4, 5)->setName("group 6");
validateOutput(sheet, "nestedpartgroups.xml");
delete sheet;
}
void testNoteDurations()
{
Sheet* sheet = new Sheet();
Bar* bar = sheet->addBar();
Part* part = sheet->addPart("part");
Voice* voice = part->addVoice();
Staff* staff = part->addStaff();
VoiceBar* vb = bar->voice(voice);
for (Duration d = HundredTwentyEighthNote; d <= BreveNote; d = (Duration)(d + 1)) {
Chord* c = new Chord(d);
c->addNote(staff, 0);
vb->addElement(c);
}
for (int i = 1; i < 4; i++) {
Chord* c = new Chord(QuarterNote, i);
c->addNote(staff, 0);
vb->addElement(c);
}
validateOutput(sheet, "notedurations.xml");
delete sheet;
}
void testNotePitch()
{
Sheet* sheet = new Sheet();
Bar* bar = sheet->addBar();
Part* part = sheet->addPart("part");
Voice* voice = part->addVoice();
Staff* staff = part->addStaff();
VoiceBar* vb = bar->voice(voice);
for (int p = -20; p <= 20; p++) {
Chord* c = new Chord(QuarterNote);
c->addNote(staff, p);
vb->addElement(c);
}
validateOutput(sheet, "notepitch.xml");
delete sheet;
}
void testNoteAccidentals()
{
Sheet* sheet = new Sheet();
Bar* bar = sheet->addBar();
Part* part = sheet->addPart("part");
Voice* voice = part->addVoice();
Staff* staff = part->addStaff();
VoiceBar* vb = bar->voice(voice);
for (int a = -2; a <= 2; a++) {
Chord* c = new Chord(QuarterNote);
c->addNote(staff, 0, a);
vb->addElement(c);
}
validateOutput(sheet, "noteaccidentals.xml");
delete sheet;
}
};
#define FAIL(message) do { QTest::qFail(message, __FILE__, __LINE__); return false; } while (0)
bool compareNodes(KoXmlNode& valid, KoXmlNode& result, QString path)
{
path += '/' + valid.nodeName();
if (result.localName() != valid.localName()) {
FAIL(QString("nodeName does not match at %1; expected %2, received %3").arg(path, valid.nodeName(), result.nodeName()).toLocal8Bit().constData());
}
if (result.namespaceURI() != valid.namespaceURI()) {
FAIL(QString("namespace does not match at %1; expected %2, received %3").arg(path, valid.namespaceURI(), result.namespaceURI()).toLocal8Bit().constData());
}
if (result.isCDATASection()) {
if (!valid.isCDATASection()) {
FAIL(QString("node value types differ").toLocal8Bit().constData());
} else {
if (result.toCDATASection().data() != valid.toCDATASection().data()) {
FAIL(QString("node value does not match at %1; expected %2, received %3").arg(path, valid.toCDATASection().data(), result.toCDATASection().data()).toLocal8Bit().constData());
}
}
} else if (result.isText()) {
if (!valid.isText()) {
FAIL(QString("node value types differ").toLocal8Bit().constData());
} else {
if (result.toText().data() != valid.toText().data()) {
FAIL(QString("node value does not match at %1; expected %2, received %3").arg(path, valid.toText().data(), result.toText().data()).toLocal8Bit().constData());
}
}
}
// if comparing identification nodes, simply return as the contents is not really relevant
if (result.nodeName() == "identification") return true;
// compare attributes
KoXmlElement r = result.toElement();
KoXmlElement v = valid.toElement();
if (!r.isNull() && !v.isNull()) {
foreach (const QString &attr, KoXml::attributeNames(v)) {
if (r.attribute(attr) != v.attribute(attr)) {
FAIL(QString("incorrect attribute %1 for %2; expected %3, received %4").arg(attr, path, v.attribute(attr), r.attribute(attr)).toLocal8Bit().constData());
}
}
foreach (const QString &attr, KoXml::attributeNames(r)) {
if (!v.hasAttribute(attr)) {
FAIL(QString("incorrect attribute %1 for %2; expected %3, received %4").arg(attr, path, v.attribute(attr), r.attribute(attr)).toLocal8Bit().constData());
}
}
}
// compare child nodes
if (KoXml::childNodesCount(result) != KoXml::childNodesCount(valid)) {
FAIL(QString("childNodesCount does not match at %1; expected %2, received %3")
.arg(path).arg(KoXml::childNodesCount(valid)).arg(KoXml::childNodesCount(result)).toLocal8Bit().constData());
}
int idx = 0;
for (KoXmlNode rChild = result.firstChild(), vChild = valid.firstChild(); !rChild.isNull() || !vChild.isNull(); rChild = rChild.nextSibling(), vChild = vChild.nextSibling()) {
if (!compareNodes(vChild, rChild, QString(path + "[%1]").arg(idx++))) return false;
}
return true;
}
bool validateOutput(Sheet* sheet, const char* fname)
{
MusicCore::MusicXmlWriter writer;
QIODevice* dev = new QBuffer();
dev->open(QIODevice::ReadWrite);
KoXmlWriter xmlWriter(dev);
xmlWriter.startDocument("score-partwise", "-//Recordare//DTD MusicXML 1.1 Partwise//EN",
"http://www.musicxml.org/dtds/partwise.dtd");
writer.writeSheet(xmlWriter, sheet);
xmlWriter.endDocument();
QFile validFile(QString(KDESRCDIR "/files/%1").arg(fname));
validFile.open(QIODevice::ReadOnly);
KoXmlDocument valid;
KoXml::setDocument(valid, &validFile, true);
dev->reset();
KoXmlDocument result;
KoXml::setDocument(result, dev, true);
bool res = compareNodes(valid, result);
if (!res) {
QFile f(QString(KDESRCDIR "/files/out_%1").arg(fname));
f.open(QIODevice::WriteOnly);
f.write(((QBuffer*)dev)->data());
f.close();
}
delete dev;
return res;
}
QTEST_MAIN(MusicXmlWriterTest)
#include <MusicXmlWriterTest.moc>
diff --git a/plugins/musicshape/core/tests/SheetTest.cpp b/plugins/musicshape/core/tests/SheetTest.cpp
index c804d1c3c7d..41eaf1d0953 100644
--- a/plugins/musicshape/core/tests/SheetTest.cpp
+++ b/plugins/musicshape/core/tests/SheetTest.cpp
@@ -1,177 +1,177 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
-#include <QtTest>
+#include <QTest>
#include "Sheet.h"
#include "Part.h"
#include "PartGroup.h"
#include "Bar.h"
#include "Voice.h"
using namespace MusicCore;
class SheetTest : public QObject
{
Q_OBJECT
private:
Sheet* sheet;
private slots:
void init()
{
sheet = new Sheet();
}
void cleanup()
{
delete sheet;
}
void testConstruction()
{
QCOMPARE(sheet->partCount(), 0);
QCOMPARE(sheet->partGroupCount(), 0);
QCOMPARE(sheet->barCount(), 0);
}
void testAddPart()
{
Part* p1 = sheet->addPart("part1");
Part* p2 = sheet->addPart("part2");
QCOMPARE(p1->name(), QString("part1"));
QCOMPARE(p1->sheet(), sheet);
QCOMPARE(sheet->partCount(), 2);
QCOMPARE(p1, sheet->part(0));
QCOMPARE(p2, sheet->part(1));
}
void testInsertPart()
{
Part* p1 = sheet->insertPart(0, "part1");
Part* p2 = sheet->insertPart(0, "part2");
Part* p3 = sheet->insertPart(1, "part3");
QCOMPARE(p1->name(), QString("part1"));
QCOMPARE(p1->sheet(), sheet);
QCOMPARE(sheet->partCount(), 3);
QCOMPARE(p2, sheet->part(0));
QCOMPARE(p3, sheet->part(1));
QCOMPARE(p1, sheet->part(2));
}
void testRemovePart_index()
{
sheet->addPart("part1");
Part* p2 = sheet->addPart("part2");
sheet->removePart(0);
QCOMPARE(sheet->partCount(), 1);
QCOMPARE(sheet->part(0), p2);
}
void testRemovePart_part()
{
Part* p1 = sheet->addPart("part1");
Part* p2 = sheet->addPart("part2");
sheet->removePart(p1);
QCOMPARE(sheet->partCount(), 1);
QCOMPARE(sheet->part(0), p2);
}
void testAddPartGroup()
{
sheet->addPart("part 1");
sheet->addPart("part 2");
PartGroup *pg1 = sheet->addPartGroup(0, 1);
PartGroup *pg2 = sheet->addPartGroup(0, 1);
QCOMPARE(pg1->sheet(), sheet);
QCOMPARE(sheet->partGroupCount(), 2);
QCOMPARE(sheet->partGroup(0), pg1);
QCOMPARE(sheet->partGroup(1), pg2);
}
void testRemovePartGroup()
{
sheet->addPart("part 1");
sheet->addPart("part 2");
PartGroup *pg1 = sheet->addPartGroup(0, 1);
PartGroup *pg2 = sheet->addPartGroup(0, 1);
sheet->removePartGroup(pg1);
QCOMPARE(sheet->partGroupCount(), 1);
QCOMPARE(sheet->partGroup(0), pg2);
}
void testAddBar()
{
Bar* bar = sheet->addBar();
QCOMPARE(bar->sheet(), sheet);
QCOMPARE(sheet->barCount(), 1);
QCOMPARE(sheet->bar(0), bar);
}
void testAddBars()
{
sheet->addBars(3);
QCOMPARE(sheet->barCount(), 3);
QCOMPARE(sheet->bar(0)->sheet(), sheet);
}
void testInsertBar()
{
Bar* b1 = sheet->insertBar(0);
Bar* b2 = sheet->insertBar(0);
Bar* b3 = sheet->insertBar(1);
QCOMPARE(sheet->barCount(), 3);
QCOMPARE(sheet->bar(0)->sheet(), sheet);
QCOMPARE(sheet->bar(0), b2);
QCOMPARE(sheet->bar(1), b3);
QCOMPARE(sheet->bar(2), b1);
}
void testRemoveBar()
{
sheet->addBars(3);
Bar* b = sheet->bar(2);
sheet->removeBar(1);
QCOMPARE(sheet->barCount(), 2);
QCOMPARE(sheet->bar(1), b);
}
void testRemoveBars()
{
sheet->addBars(4);
Bar* b = sheet->bar(3);
sheet->removeBars(1, 2);
QCOMPARE(sheet->barCount(), 2);
QCOMPARE(sheet->bar(1), b);
}
};
QTEST_MAIN(SheetTest)
#include <SheetTest.moc>
diff --git a/plugins/musicshape/core/tests/VoiceBarTest.cpp b/plugins/musicshape/core/tests/VoiceBarTest.cpp
index 8bcbe48de62..62c10801ee7 100644
--- a/plugins/musicshape/core/tests/VoiceBarTest.cpp
+++ b/plugins/musicshape/core/tests/VoiceBarTest.cpp
@@ -1,135 +1,135 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Marijn Kruisselbrink <mkruisselbrink@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
-#include <QtTest>
+#include <QTest>
#include "VoiceBar.h"
#include "Sheet.h"
#include "Part.h"
#include "Voice.h"
#include "VoiceElement.h"
#include "Bar.h"
using namespace MusicCore;
class VoiceBarTest : public QObject
{
Q_OBJECT
private:
MusicCore::VoiceBar* voiceBar;
private slots:
void init()
{
Sheet* sheet = new Sheet();
Bar* bar = new Bar(sheet);
voiceBar = new VoiceBar(bar);
}
void cleanup()
{
delete voiceBar;
}
void testConstruction()
{
// QCOMPARE(voiceBar->voice(), voice);
// QCOMPARE(voiceBar->bar(), bar);
QCOMPARE(voiceBar->elementCount(), 0);
}
void testAddElement()
{
VoiceElement *elem1 = new VoiceElement(), *elem2 = new VoiceElement();
voiceBar->addElement(elem1);
QCOMPARE(voiceBar->elementCount(), 1);
QCOMPARE(voiceBar->element(0), elem1);
voiceBar->addElement(elem2);
QCOMPARE(voiceBar->elementCount(), 2);
QCOMPARE(voiceBar->element(1), elem2);
QCOMPARE(voiceBar->element(0), elem1);
}
void testInsertElement_index()
{
VoiceElement *elem1 = new VoiceElement(), *elem2 = new VoiceElement(), *elem3 = new VoiceElement();
voiceBar->insertElement(elem1, 0);
QCOMPARE(voiceBar->elementCount(), 1);
QCOMPARE(voiceBar->element(0), elem1);
voiceBar->insertElement(elem2, 0);
QCOMPARE(voiceBar->elementCount(), 2);
QCOMPARE(voiceBar->element(0), elem2);
QCOMPARE(voiceBar->element(1), elem1);
voiceBar->insertElement(elem3, 1);
QCOMPARE(voiceBar->elementCount(), 3);
QCOMPARE(voiceBar->element(0), elem2);
QCOMPARE(voiceBar->element(1), elem3);
QCOMPARE(voiceBar->element(2), elem1);
}
void testInsertElement_element()
{
VoiceElement *elem1 = new VoiceElement(), *elem2 = new VoiceElement(), *elem3 = new VoiceElement();
voiceBar->addElement(elem1);
voiceBar->insertElement(elem2, elem1);
QCOMPARE(voiceBar->elementCount(), 2);
QCOMPARE(voiceBar->element(0), elem2);
QCOMPARE(voiceBar->element(1), elem1);
voiceBar->insertElement(elem3, elem1);
QCOMPARE(voiceBar->elementCount(), 3);
QCOMPARE(voiceBar->element(0), elem2);
QCOMPARE(voiceBar->element(1), elem3);
QCOMPARE(voiceBar->element(2), elem1);
}
void testRemoveElement_index()
{
VoiceElement *elem1 = new VoiceElement(), *elem2 = new VoiceElement(), *elem3 = new VoiceElement();
voiceBar->addElement(elem1);
voiceBar->addElement(elem2);
voiceBar->addElement(elem3);
voiceBar->removeElement(1);
QCOMPARE(voiceBar->elementCount(), 2);
QCOMPARE(voiceBar->element(0), elem1);
QCOMPARE(voiceBar->element(1), elem3);
}
void testRemoveElement_element()
{
VoiceElement *elem1 = new VoiceElement(), *elem2 = new VoiceElement(), *elem3 = new VoiceElement();
voiceBar->addElement(elem1);
voiceBar->addElement(elem2);
voiceBar->addElement(elem3);
voiceBar->removeElement(elem2);
QCOMPARE(voiceBar->elementCount(), 2);
QCOMPARE(voiceBar->element(0), elem1);
QCOMPARE(voiceBar->element(1), elem3);
}
};
QTEST_MAIN(VoiceBarTest)
#include <VoiceBarTest.moc>
diff --git a/plugins/spacenavigator/SpaceNavigatorDevice.cpp b/plugins/spacenavigator/SpaceNavigatorDevice.cpp
index 2ca437b2ee9..d80f7d0013c 100644
--- a/plugins/spacenavigator/SpaceNavigatorDevice.cpp
+++ b/plugins/spacenavigator/SpaceNavigatorDevice.cpp
@@ -1,110 +1,106 @@
/* This file is part of the KDE project
* Copyright (c) 2008 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "SpaceNavigatorDevice.h"
#include "SpaceNavigatorPollingThread.h"
#include "SpaceNavigatorEvent.h"
#include <KoToolManager.h>
#include <KoCanvasController.h>
#include <kdebug.h>
#include <spnav.h>
#include <math.h>
#define SpaceNavigatorDevice_ID "SpaceNavigator"
SpaceNavigatorDevice::SpaceNavigatorDevice( QObject * parent )
: KoInputDeviceHandler( parent, SpaceNavigatorDevice_ID ), m_thread( new SpaceNavigatorPollingThread( this ) )
{
qRegisterMetaType<Qt::MouseButtons>( "Qt::MouseButtons" );
qRegisterMetaType<Qt::MouseButton>( "Qt::MouseButton" );
connect( m_thread, SIGNAL(moveEvent(int,int,int,int,int,int,Qt::MouseButtons)),
this, SLOT(slotMoveEvent(int,int,int,int,int,int,Qt::MouseButtons)));
connect( m_thread, SIGNAL(buttonEvent(int,int,int,int,int,int,Qt::MouseButtons,Qt::MouseButton,int)),
this, SLOT(slotButtonEvent(int,int,int,int,int,int,Qt::MouseButtons,Qt::MouseButton,int)));
}
SpaceNavigatorDevice::~SpaceNavigatorDevice()
{
}
bool SpaceNavigatorDevice::start()
{
- kDebug() << "starting spacenavigator device...";
-
if( m_thread->isRunning() )
return true;
m_thread->start();
return true;
}
bool SpaceNavigatorDevice::stop()
{
- kDebug() << "stopping spacenavigator device...";
-
if( ! m_thread->isRunning() )
return true;
m_thread->stop();
if( ! m_thread->wait( 500 ) )
m_thread->terminate();
spnav_close();
return true;
}
void SpaceNavigatorDevice::slotMoveEvent( int x, int y, int z, int rx, int ry, int rz, Qt::MouseButtons buttons )
{
SpaceNavigatorEvent e( KoInputDeviceHandlerEvent::PositionChanged );
e.setPosition( x, y, z );
e.setRotation( rx, ry, rz );
e.setButton( Qt::NoButton );
e.setButtons( buttons );
KoToolManager::instance()->injectDeviceEvent( &e );
if( ! e.isAccepted() )
{
// no tool wants the event, so do some standard actions
KoCanvasController * controller = KoToolManager::instance()->activeCanvasController();
// check if the z-movement is dominant
if( qAbs(z) > qAbs(x) && qAbs(z) > qAbs(y) )
{
// zoom
controller->zoomBy( controller->preferredCenter().toPoint(), pow(1.01,-z/10) );
}
else
{
// pan
controller->pan( QPoint( -x, -y ) );
}
}
}
void SpaceNavigatorDevice::slotButtonEvent( int x, int y, int z, int rx, int ry, int rz, Qt::MouseButtons buttons, Qt::MouseButton button, int type )
{
SpaceNavigatorEvent e( static_cast<KoInputDeviceHandlerEvent::Type>( type ) );
e.setPosition( x, y, z );
e.setRotation( rx, ry, rz );
e.setButton( button );
e.setButtons( buttons );
KoToolManager::instance()->injectDeviceEvent( &e );
}
diff --git a/plugins/textediting/spellcheck/tests/TestSpellCheck.cpp b/plugins/textediting/spellcheck/tests/TestSpellCheck.cpp
index e3cc924f2be..01de7b0ccb8 100644
--- a/plugins/textediting/spellcheck/tests/TestSpellCheck.cpp
+++ b/plugins/textediting/spellcheck/tests/TestSpellCheck.cpp
@@ -1,95 +1,97 @@
#include "TestSpellCheck.h"
#include "../BgSpellCheck.h"
#include <KoCharacterStyle.h>
#include <QTextDocument>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextCharFormat>
+#include <QTest>
+
class MySpellCheck : public BgSpellCheck
{
public:
MySpellCheck(QObject *parent = 0) : BgSpellCheck(parent)
{
}
QString publicFetchMoreText() {
return fetchMoreText();
}
virtual void start() { }
};
void TestSpellCheck::testFetchMoreText()
{
MySpellCheck checker;
QTextDocument doc;
QString text("some simple text\na second parag with more text\n");
doc.setPlainText(text);
checker.startRun(&doc, 0, text.size());
QTextBlock block = doc.begin();
QCOMPARE(checker.publicFetchMoreText(), block.text());
block = block.next();
QVERIFY(block.isValid());
QCOMPARE(checker.publicFetchMoreText(), block.text());
QTextCursor cursor(&doc);
QTextCharFormat cf;
cf.setProperty(KoCharacterStyle::Language, QVariant("pl"));
cursor.setPosition(4, QTextCursor::KeepAnchor);
cursor.mergeCharFormat(cf);
checker.startRun(&doc, 0, text.size());
block = doc.begin();
QCOMPARE(checker.publicFetchMoreText(), QString("some"));
QCOMPARE(checker.publicFetchMoreText(), block.text().mid(4));
block = block.next();
QVERIFY(block.isValid());
QCOMPARE(checker.publicFetchMoreText(), block.text());
// add some more
cursor.setPosition(block.position() + 2);
cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); // 'second'
int position2 = cursor.anchor();
int position3 = cursor.position();
cf.setProperty(KoCharacterStyle::Language, QVariant("br"));
cursor.mergeCharFormat(cf);
cursor.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor, 2);
cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); // 'with'
cf.setProperty(KoCharacterStyle::Language, QVariant("en"));
cursor.mergeCharFormat(cf);
checker.startRun(&doc, 0, text.size());
checker.setDefaultLanguage("en_NZ");
block = doc.begin();
QCOMPARE(checker.publicFetchMoreText(), QString("some"));
QCOMPARE(checker.publicFetchMoreText(), block.text().mid(4));
block = block.next();
QVERIFY(block.isValid());
QCOMPARE(checker.publicFetchMoreText(), block.text().left(2));
QCOMPARE(checker.publicFetchMoreText(), text.mid(position2, position3 - position2));
QCOMPARE(checker.publicFetchMoreText(), text.mid(position3).trimmed());
// start a check in the middle of a block
checker.startRun(&doc, 4, 19);
block = doc.begin();
QCOMPARE(checker.publicFetchMoreText(), block.text().mid(4));
block = block.next();
QVERIFY(block.isValid());
QCOMPARE(checker.publicFetchMoreText(), block.text().left(2));
}
void TestSpellCheck::testFetchMoreText2()
{
MySpellCheck checker;
QTextDocument doc;
doc.setPlainText("\n\n\nMostly Empty Parags.\n\n");
checker.startRun(&doc, 0, 20);
checker.setDefaultLanguage("en_NZ");
QCOMPARE(checker.publicFetchMoreText(), QString("Mostly Empty Parags."));
}
-QTEST_KDEMAIN(TestSpellCheck, GUI)
+QTEST_MAIN(TestSpellCheck)
diff --git a/plugins/textediting/spellcheck/tests/TestSpellCheck.h b/plugins/textediting/spellcheck/tests/TestSpellCheck.h
index f02a068392e..a709ec617a3 100644
--- a/plugins/textediting/spellcheck/tests/TestSpellCheck.h
+++ b/plugins/textediting/spellcheck/tests/TestSpellCheck.h
@@ -1,18 +1,17 @@
#ifndef TESTSPELLCHECK_H
#define TESTSPELLCHECK_H
#include <QObject>
-#include <qtest_kde.h>
class TestSpellCheck : public QObject
{
Q_OBJECT
public:
TestSpellCheck() {}
private Q_SLOTS:
void testFetchMoreText();
void testFetchMoreText2();
};
#endif
diff --git a/sheets/tests/BenchmarkCluster.cpp b/sheets/tests/BenchmarkCluster.cpp
index dc533d29bff..b4d59f34a7f 100644
--- a/sheets/tests/BenchmarkCluster.cpp
+++ b/sheets/tests/BenchmarkCluster.cpp
@@ -1,253 +1,255 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "BenchmarkHelper.h"
#include "Cluster.h"
#include "Global.h"
#include "BenchmarkCluster.h"
+#include <QTest>
+
using namespace KSpread;
class Cell;
void ClusterBenchmark::testInsertionPerformance()
{
Cluster storage;
Cell* cell = 0;
qDebug() << "measuring loading-like insertion...";
Time::tval start = 0;
Time::tval ticks = 0;
int col = 1;
int row = 1;
int cols = 100;
int rows = 10000;
long counter = 0;
start = Time::stamp();
for (int r = row; r <= rows; ++r) {
for (int c = col; c <= cols; c += 1) {
storage.insert(cell, c, r);
counter += 1;
}
}
ticks = Time::elapsed(start);
qDebug() << qPrintable(Time::printAverage(ticks, counter));
qDebug() << "measuring random singular insertion...";
storage.clear();
counter = 0;
while (counter < Time::iterations) {
col = 1 + rand() % 1000;
row = 1 + rand() % 1000;
cols = col + 1;
rows = row + 1;
start = Time::stamp();
for (int r = row; r <= rows; ++r) {
for (int c = col; c <= cols && counter < Time::iterations; c += 1) {
storage.insert(cell, c, r);
counter += 1;
}
}
ticks += Time::elapsed(start);
}
qDebug() << qPrintable(Time::printAverage(ticks, counter));
}
void ClusterBenchmark::testLookupPerformance()
{
// row x column
const int scenarios[] = {
#if 1
1000, 1000 // large
#else
5, 5, // very small
30, 20, // fit to screen
100, 100, // medium
1000, 1000, // large
10000, 100, // typical data: more rows
10000, 2000, // and 20 times larger
100, 10000, // not really typical: more columns
8000, 8000 // hopelessly large
#endif
};
Cluster storage;
Cell* cell = 0;
for (uint sc = 0; sc < sizeof(scenarios) / sizeof(scenarios[0]) / 2; sc++) {
int maxrow = scenarios[sc*2];
int maxcol = scenarios[sc*2+1];
storage.clear();
#if 0
for (int r = 0; r < maxrow; ++r) {
for (int c = 0; c < maxcol; ++c) {
storage.insert(cell, c, r);
}
}
#else
storage.insert(cell, 1, 1);
storage.insert(cell, maxcol / 2, maxrow / 2);
storage.insert(cell, maxcol / 3, maxrow / 3);
storage.insert(cell, maxcol, maxrow);
#endif
// qDebug() << endl << qPrintable( storage.dump() );
QString prefix = QString("%1 x %2").arg(maxrow).arg(maxcol);
qDebug() << "start measuring..." << prefix;
Time::tval start = 0;
Time::tval ticks = 0;
Cell* v;
int col = 0;
int row = 0;
int cols = 0;
int rows = 0;
long counter = 0;
while (counter < Time::iterations) {
col = 1 + rand() % maxcol;
row = 1 + rand() % maxrow;
cols = col + 1 * (rand() % 10);
rows = row + rand() % 10;
start = Time::stamp();
for (int r = row; r <= rows && counter < Time::iterations; ++r) {
for (int c = col; c <= cols && counter < Time::iterations; c += 1) {
v = storage.lookup(c, r);
counter += 1;
}
}
ticks += Time::elapsed(start);
}
qDebug() << qPrintable(Time::printAverage(ticks, counter, prefix));
}
}
void ClusterBenchmark::testInsertColumnsPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int c = 1; c <= KS_colMax; ++c)
storage.insert(cell, c, 1);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 100; ++i)
storage.insertColumn(250); // near a cluster border
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 100));
}
void ClusterBenchmark::testDeleteColumnsPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int c = 1; c <= KS_colMax; ++c)
storage.insert(cell, c, 1);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 100; ++i)
storage.removeColumn(250); // near a cluster border
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 100));
}
void ClusterBenchmark::testInsertRowsPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int r = 1; r <= KS_rowMax; ++r)
storage.insert(cell, 1, r);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 100; ++i)
storage.insertRow(250); // near a cluster border
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 100));
}
void ClusterBenchmark::testDeleteRowsPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int r = 1; r <= KS_rowMax; ++r)
storage.insert(cell, 1, r);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 100; ++i)
storage.removeRow(250); // near a cluster border
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 100));
}
void ClusterBenchmark::testShiftLeftPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int c = 1; c <= KS_colMax; ++c)
storage.insert(cell, c, 1);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 1000; ++i)
storage.removeShiftLeft(QPoint(42, 1));
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 1000));
}
void ClusterBenchmark::testShiftRightPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int c = 1; c <= KS_colMax; ++c)
storage.insert(cell, c, 1);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 1000; ++i)
storage.insertShiftRight(QPoint(42, 1));
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 1000));
}
void ClusterBenchmark::testShiftUpPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int r = 1; r <= KS_rowMax; ++r)
storage.insert(cell, 1, r);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 1000; ++i)
storage.removeShiftUp(QPoint(1, 42));
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 1000));
}
void ClusterBenchmark::testShiftDownPerformance()
{
Cluster storage;
Cell* cell = 0;
for (int r = 1; r <= KS_rowMax; ++r)
storage.insert(cell, 1, r);
qDebug() << "start measuring...";
Time::tval start = Time::stamp();
for (int i = 1; i < 1000; ++i)
storage.insertShiftDown(QPoint(1, 42));
qDebug() << qPrintable(Time::printAverage(Time::elapsed(start), 1000));
}
QTEST_MAIN(ClusterBenchmark)
diff --git a/sheets/tests/BenchmarkCluster.h b/sheets/tests/BenchmarkCluster.h
index 83a5c4636a7..14440f6da0b 100644
--- a/sheets/tests/BenchmarkCluster.h
+++ b/sheets/tests/BenchmarkCluster.h
@@ -1,47 +1,46 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KSPREAD_CLUSTER_BENCHMARK
#define KSPREAD_CLUSTER_BENCHMARK
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace KSpread
{
class ClusterBenchmark : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testInsertionPerformance();
void testLookupPerformance();
void testInsertColumnsPerformance();
void testDeleteColumnsPerformance();
void testInsertRowsPerformance();
void testDeleteRowsPerformance();
void testShiftLeftPerformance();
void testShiftRightPerformance();
void testShiftUpPerformance();
void testShiftDownPerformance();
};
} // namespace KSpread
#endif // KSPREAD_CLUSTER_BENCHMARK
diff --git a/sheets/tests/BenchmarkPointStorage.cpp b/sheets/tests/BenchmarkPointStorage.cpp
index 853bf8bf7cd..ab0452cafdb 100644
--- a/sheets/tests/BenchmarkPointStorage.cpp
+++ b/sheets/tests/BenchmarkPointStorage.cpp
@@ -1,266 +1,268 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QUuid>
#include "calligra_sheets_limits.h"
#include "PointStorage.h"
#include "BenchmarkPointStorage.h"
+#include <QTest>
+
using namespace Calligra::Sheets;
void PointStorageBenchmark::testInsertionPerformance_loadingLike()
{
PointStorage<int> storage;
int col = 1;
int row = 1;
int cols = 100;
int rows = 10000;
QBENCHMARK {
for (int r = row; r <= rows; ++r) {
for (int c = col; c <= cols; c += 1) {
storage.insert(c, r, c);
}
}
}
}
void PointStorageBenchmark::testInsertionPerformance_singular()
{
PointStorage<int> storage;
QBENCHMARK {
int col = 1 + rand() % 1000;
int row = 1 + rand() % 1000;
int cols = col + 1;
int rows = row + 1;
for (int r = row; r <= rows; ++r) {
for (int c = col; c <= cols; c += 1) {
storage.insert(c, r, c);
}
}
}
}
void PointStorageBenchmark::testLookupPerformance_data()
{
QTest::addColumn<int>("maxrow");
QTest::addColumn<int>("maxcol");
QTest::newRow("very small") << 5 << 5;
QTest::newRow("fit to screen") << 30 << 20;
QTest::newRow("medium") << 100 << 100;
QTest::newRow("large") << 1000 << 1000;
QTest::newRow("typical data: more rows") << 10000 << 100;
QTest::newRow("20 times larger") << 10000 << 2000;
QTest::newRow("not really typical: more columns") << 100 << 10000;
QTest::newRow("hopelessly large") << 8000 << 8000;
QTest::newRow("some complete columns; KS_colMax-10, because of max lookup range of width 10 below") << 10 << 32757;
QTest::newRow("some complete rows; KS_rowMax-10, because of max lookup range of height 10 below") << 32757 << 10;
}
void PointStorageBenchmark::testLookupPerformance()
{
PointStorage<int> storage;
QFETCH(int, maxrow);
QFETCH(int, maxcol);
for (int r = 0; r < maxrow; ++r) {
for (int c = 0; c < maxcol; ++c) {
storage.m_data << c;
storage.m_cols << (c + 1);
}
storage.m_rows << r*maxcol;
}
// qDebug() << endl << qPrintable( storage.dump() );
int v;
int col = 0;
int row = 0;
int cols = 0;
int rows = 0;
QBENCHMARK {
col = 1 + rand() % maxcol;
row = 1 + rand() % maxrow;
cols = col + 1 * (rand() % 10);
rows = row + rand() % 10;
for (int r = row; r <= rows; ++r) {
for (int c = col; c <= cols; c += 1) {
v = storage.lookup(c, r);
}
}
}
Q_UNUSED(v); //not fully unused but GCC thinks so, so let us just tell it so
}
void PointStorageBenchmark::testInsertColumnsPerformance()
{
PointStorage<int> storage;
for (int c = 0; c < KS_colMax; ++c) {
storage.m_data << 1;
storage.m_cols << 1;
}
storage.m_rows << 0;
QBENCHMARK {
storage.insertColumns(42, 3);
}
}
void PointStorageBenchmark::testDeleteColumnsPerformance()
{
PointStorage<int> storage;
for (int c = 0; c < KS_colMax; ++c) {
storage.m_data << 1;
storage.m_cols << 1;
}
storage.m_rows << 0;
QBENCHMARK {
storage.removeColumns(42, 3);
}
}
void PointStorageBenchmark::testInsertRowsPerformance()
{
PointStorage<int> storage;
for (int r = 0; r < KS_rowMax; ++r) {
storage.m_data << 1;
storage.m_cols << 1;
storage.m_rows << r;
}
QBENCHMARK {
storage.insertRows(42, 3);
}
}
void PointStorageBenchmark::testDeleteRowsPerformance()
{
PointStorage<int> storage;
for (int r = 0; r < KS_rowMax; ++r) {
storage.m_data << 1;
storage.m_cols << 1;
storage.m_rows << r;
}
QBENCHMARK {
storage.removeRows(42, 3);
}
}
void PointStorageBenchmark::testShiftLeftPerformance()
{
PointStorage<int> storage;
for (int c = 0; c < KS_colMax; ++c) {
storage.m_data << 1;
storage.m_cols << 1;
}
storage.m_rows << 0;
QBENCHMARK {
storage.removeShiftLeft(QRect(42, 1, 3, 1));
}
}
void PointStorageBenchmark::testShiftRightPerformance()
{
PointStorage<int> storage;
for (int c = 0; c < KS_colMax; ++c) {
storage.m_data << 1;
storage.m_cols << 1;
}
storage.m_rows << 0;
QBENCHMARK {
storage.insertShiftRight(QRect(42, 1, 3, 1));
}
}
void PointStorageBenchmark::testShiftUpPerformance()
{
PointStorage<int> storage;
for (int r = 0; r < KS_rowMax; ++r) {
storage.m_data << 1;
storage.m_cols << 1;
storage.m_rows << r;
}
QBENCHMARK {
storage.removeShiftUp(QRect(1, 42, 1, 3));
}
}
void PointStorageBenchmark::testShiftDownPerformance()
{
PointStorage<int> storage;
for (int r = 0; r < KS_rowMax; ++r) {
storage.m_data << 1;
storage.m_cols << 1;
storage.m_rows << r;
}
storage.m_rows << 0;
QBENCHMARK {
storage.insertShiftDown(QRect(1, 42, 1, 3));
}
}
void PointStorageBenchmark::testIterationPerformance_data()
{
QTest::addColumn<int>("maxrow");
QTest::addColumn<int>("maxcol");
QTest::newRow("very small") << 5 << 5;
QTest::newRow("fit to screen") << 30 << 20;
QTest::newRow("medium") << 100 << 100;
QTest::newRow("large") << 1000 << 1000;
QTest::newRow("typical data: more rows") << 10000 << 100;
QTest::newRow("20 times larger") << 10000 << 2000;
QTest::newRow("not really typical: more columns") << 100 << 10000;
QTest::newRow("hopelessly large") << 8000 << 8000;
#if 0
QTest::newRow("some complete columns; KS_colMax-10, because of max lookup range of width 10 below") << 10 << 32757;
QTest::newRow("some complete rows; KS_rowMax-10, because of max lookup range of height 10 below") << 32757 << 10;
#endif
}
void PointStorageBenchmark::testIterationPerformance()
{
PointStorage<int> storage;
QFETCH(int, maxrow);
QFETCH(int, maxcol);
storage.clear();
for (int r = 0; r < maxrow; ++r) {
for (int c = 0; c < maxcol; ++c) {
storage.m_data << c;
storage.m_cols << (c + 1);
}
storage.m_rows << r*maxcol;
}
// qDebug() << endl << qPrintable( storage.dump() );
QString prefix = QString("%1 x %2").arg(maxrow).arg(maxcol);
int v;
QBENCHMARK {
for (int i = 0; i < storage.count(); ++i) {
v = storage.data(i);
}
}
Q_UNUSED(v); //Not fully unused, but GCC thinks so
}
QTEST_MAIN(PointStorageBenchmark)
diff --git a/sheets/tests/BenchmarkPointStorage.h b/sheets/tests/BenchmarkPointStorage.h
index 839b79ca02f..cb95d079d53 100644
--- a/sheets/tests/BenchmarkPointStorage.h
+++ b/sheets/tests/BenchmarkPointStorage.h
@@ -1,54 +1,53 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_POINT_STORAGE_BENCHMARK
#define CALLIGRA_SHEETS_POINT_STORAGE_BENCHMARK
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class PointStorageBenchmark : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testInsertionPerformance_loadingLike();
void testInsertionPerformance_singular();
void testLookupPerformance_data();
void testLookupPerformance();
void testInsertColumnsPerformance();
void testDeleteColumnsPerformance();
void testInsertRowsPerformance();
void testDeleteRowsPerformance();
void testShiftLeftPerformance();
void testShiftRightPerformance();
void testShiftUpPerformance();
void testShiftDownPerformance();
void testIterationPerformance_data();
void testIterationPerformance();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_POINT_STORAGE_BENCHMARK
diff --git a/sheets/tests/BenchmarkRTree.cpp b/sheets/tests/BenchmarkRTree.cpp
index 7be82067938..642018c96e7 100644
--- a/sheets/tests/BenchmarkRTree.cpp
+++ b/sheets/tests/BenchmarkRTree.cpp
@@ -1,115 +1,115 @@
/* This file is part of the KDE project
Copyright (C) 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
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., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA
*/
#include "BenchmarkRTree.h"
-#include <QtTest>
-
#include <kdebug.h>
// #include "rtree.h"
#include "RTree.h"
+#include <QTest>
+
using namespace std;
using namespace Calligra::Sheets;
void RTreeBenchmark::init()
{
RTree<double> tree;
m_tree = tree;
// insert some data in the RTree
const int max_x = 100;
const int step_x = 1;
const int max_y = 1000;
const int step_y = 1;
for (int y = 1; y <= max_y; y += step_y) { // equals row insertion into table
for (int x = 1; x <= max_x; x += step_x) { // equals cell insertion into row
m_tree.insert(QRect(x, y, step_x, step_y), 42);
}
}
}
void RTreeBenchmark::cleanup()
{
}
void RTreeBenchmark::testInsertionPerformance()
{
// reset tree for this test
RTree<double> tree;
m_tree = tree;
const int max_x = 100;
const int step_x = 1;
const int max_y = 1000;
const int step_y = 1;
QBENCHMARK {
for (int y = 1; y <= max_y; y += step_y) { // equals row insertion into table
for (int x = 1; x <= max_x; x += step_x) { // equals cell insertion into row
m_tree.insert(QRect(x, y, step_x, step_y), 42);
}
}
}
}
void RTreeBenchmark::testRowInsertionPerformance()
{
QBENCHMARK {
m_tree.insertRows(1, 5);
}
}
void RTreeBenchmark::testColumnInsertionPerformance()
{
QBENCHMARK {
m_tree.insertColumns(1, 5);
}
}
void RTreeBenchmark::testRowDeletionPerformance()
{
QBENCHMARK {
m_tree.removeRows(1, 5);
}
}
void RTreeBenchmark::testColumnDeletionPerformance()
{
QBENCHMARK {
m_tree.removeColumns(1, 5);
}
}
void RTreeBenchmark::testLookupPerformance()
{
int counter = 0;
const int max_x = 100;
const int step_x = 1;
const int max_y = 1000;
const int step_y = 1;
QBENCHMARK {
for (int y = 1; y <= max_y; y += step_y) {
for (int x = 1; x <= max_x; x += step_x) {
if (!m_tree.contains(QPoint(x, y)).isEmpty()) counter++;
}
}
}
}
QTEST_MAIN(RTreeBenchmark)
diff --git a/sheets/tests/TestBitopsFunctions.cpp b/sheets/tests/TestBitopsFunctions.cpp
index dba31399416..e631efac311 100644
--- a/sheets/tests/TestBitopsFunctions.cpp
+++ b/sheets/tests/TestBitopsFunctions.cpp
@@ -1,137 +1,139 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestBitopsFunctions.h"
#include "TestKspreadCommon.h"
+#include <QTest>
+
void TestBitopsFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x,y) { Value z(y); QCOMPARE(evaluate(x,z),(z)); }
Value TestBitopsFunctions::evaluate(const QString& formula, Value& ex)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
void TestBitopsFunctions::testBITAND()
{
// basic check of all four bit combinations
CHECK_EVAL("BITAND(12;10)", Value(8));
// test using an all-zero combo
CHECK_EVAL("BITAND(7;0)", Value(0));
// test of 31-bit value
CHECK_EVAL("BITAND(2147483641; 2147483637)", Value(2147483633));
// test of 32-bit value
CHECK_EVAL("BITAND(4294967289.0; 4294967285.0)", Value(4294967281LL));
// test of 32-bit value
CHECK_EVAL("BITAND(4294967289; 4294967285)", Value(4294967281LL));
// test of 48 bit value
CHECK_EVAL("BITAND(281474976710649 ; 281474976710645)", Value(281474976710641LL));
// test of 48 bit value
CHECK_EVAL("BITAND(281474976710655; 281474976710655)", Value(281474976710655LL));
// test of 48 bit value
CHECK_EVAL("BITAND(281474976710655; 281474976710655)<>281474976710656", Value(true));
}
void TestBitopsFunctions::testBITOR()
{
// basic check of all four bit combinations
CHECK_EVAL("BITOR(12;10)", Value(14));
// test using an all-zero combo
CHECK_EVAL("BITOR(7;0)", Value(7));
// test of 31-bit value
CHECK_EVAL("BITOR(2147483641; 2147483637)", Value(2147483645));
// test of 32-bit value
CHECK_EVAL("BITOR(4294967289.0; 4294967285.0)", Value(4294967293LL));
// test of 32-bit value
CHECK_EVAL("BITOR(4294967289; 4294967285)", Value(4294967293LL));
// test of 48 bit value
CHECK_EVAL("BITOR(281474976710649; 281474976710645)", Value(281474976710653LL));
// test of 48 bit value
CHECK_EVAL("BITOR(281474976710655; 281474976710655)", Value(281474976710655LL));
// test of 48 bit value
CHECK_EVAL("BITOR(281474976710655; 281474976710655)<>281474976710656", Value(true));
}
void TestBitopsFunctions::testBITXOR()
{
// basic check of all four bit combinations
CHECK_EVAL("BITXOR(12;10)", Value(6));
// test using an all-zero combo
CHECK_EVAL("BITXOR(7;0)", Value(7));
// test of 31-bit value
CHECK_EVAL("BITXOR(2147483641; 2)", Value(2147483643));
// test of 32-bit value
CHECK_EVAL("BITXOR(4294967289.0; 2.0)", Value(4294967291LL));
// test of 32-bit value
CHECK_EVAL("BITXOR(4294967289; 2)", Value(4294967291LL));
// test of 48 bit value
CHECK_EVAL("BITXOR(281474976710649 ; 2)", Value(281474976710651LL));
// test of 48 bit value
CHECK_EVAL("BITXOR(281474976710655; 0)", Value(281474976710655LL));
// test of 48 bit value
CHECK_EVAL("BITXOR(281474976710655; 0)<>281474976710656", Value(true));
}
void TestBitopsFunctions::testBITLSHIFT()
{
CHECK_EVAL("BITLSHIFT(63;2)", Value(252));
CHECK_EVAL("BITLSHIFT(63;0)", Value(63));
CHECK_EVAL("BITLSHIFT(63;-2)", Value(15));
CHECK_EVAL("BITLSHIFT(1;47)", Value(140737488355328LL));
// test for 31 bits
CHECK_EVAL("BITLSHIFT(2147483641; 0)", Value(2147483641LL));
// test for 32 bits
CHECK_EVAL("BITLSHIFT(4294967289; 0)", Value(4294967289LL));
// test for 48 bits
CHECK_EVAL("BITLSHIFT(281474976710649; 0)", Value(281474976710649LL));
}
void TestBitopsFunctions::testBITRSHIFT()
{
CHECK_EVAL("BITRSHIFT(63;2)", Value(15));
CHECK_EVAL("BITRSHIFT(63;0)", Value(63));
CHECK_EVAL("BITRSHIFT(63;-2)", Value(252));
CHECK_EVAL("BITRSHIFT(63;48)", Value(0));
// test for 31 bits
CHECK_EVAL("BITRSHIFT(2147483641; 0)", Value(2147483641LL));
// test for 32 bits
CHECK_EVAL("BITRSHIFT(4294967289; 0)", Value(4294967289LL));
// test for 48 bits
CHECK_EVAL("BITRSHIFT(281474976710649 ; 0)", Value(281474976710649LL));
}
-QTEST_KDEMAIN(TestBitopsFunctions, GUI)
+QTEST_MAIN(TestBitopsFunctions)
diff --git a/sheets/tests/TestBitopsFunctions.h b/sheets/tests/TestBitopsFunctions.h
index bc0a9a0dfac..24c04b786ba 100644
--- a/sheets/tests/TestBitopsFunctions.h
+++ b/sheets/tests/TestBitopsFunctions.h
@@ -1,51 +1,50 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_BITOPS_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_BITOPS_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestBitopsFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testBITAND();
void testBITOR();
void testBITXOR();
void testBITLSHIFT();
void testBITRSHIFT();
private:
Value evaluate(const QString&, Value& ex);
};
} // namespace Sheets
} // namespace Calligra
#endif
diff --git a/sheets/tests/TestCell.cpp b/sheets/tests/TestCell.cpp
index 059cb2a9aec..6768e1ad353 100644
--- a/sheets/tests/TestCell.cpp
+++ b/sheets/tests/TestCell.cpp
@@ -1,116 +1,116 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
Copyright 2011 Sebastian Sauer <sebastian.sauer@kdab.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestCell.h"
#include <KoStore.h>
#include <KoOdfStylesReader.h>
#include <KoOdfLoadingContext.h>
#include <KoShapeLoadingContext.h>
#include <KoDocumentResourceManager.h>
#include <sheets/Cell.h>
#include <sheets/CellStorage.h>
#include <sheets/Map.h>
#include <sheets/Sheet.h>
#include <sheets/Style.h>
#include <sheets/OdfLoadingContext.h>
#include <sheets/Value.h>
-#include <qtest_kde.h>
+#include <QTest>
using namespace Calligra::Sheets;
KoXmlDocument CellTest::xmlDocument(const QString &content)
{
KoXmlDocument document;
QString xml = "<table:table-cell xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" >" + content + "</table:table-cell>";
bool ok = document.setContent(xml, true);
return ok ? document : KoXmlDocument();
}
void CellTest::testRichText()
{
KoOdfStylesReader stylesReader;
QBuffer buffer;
buffer.open(QIODevice::ReadOnly);
KoStore *store = KoStore::createStore(&buffer, KoStore::Read);
KoOdfLoadingContext odfContext(stylesReader, store);
OdfLoadingContext context(odfContext);
KoDocumentResourceManager documentResources;
KoShapeLoadingContext shapeContext(odfContext, &documentResources);
context.shapeContext = &shapeContext;
Styles autoStyles;
QString cellStyleName;
Map map;
Sheet* sheet = map.addNewSheet();
CellStorage* storage = sheet->cellStorage();
storage->setValue(1, 1, Value(1));
Cell cell = storage->firstInRow(1);
QVERIFY(!cell.isNull());
{ // Test the simple case. Only one paragraph with some simple text.
KoXmlDocument doc = xmlDocument("<text:p>Some text</text:p>");
KoXmlElement e = doc.documentElement();
QVERIFY(!e.isNull());
cell.loadOdfCellText(e, context, autoStyles, cellStyleName);
QVERIFY(!cell.isNull());
QVERIFY(!cell.richText());
QVERIFY(cell.userInput().split('\n').count() == 1);
}
{ // Text in the paragraph and in a child text:span means rich-text.
KoXmlDocument doc = xmlDocument("<text:p>First<text:span>Second<text:span>Theird</text:span></text:span></text:p>");
KoXmlElement e = doc.documentElement();
QVERIFY(!e.isNull());
cell.loadOdfCellText(e, context, autoStyles, cellStyleName);
QVERIFY(!cell.isNull());
QVERIFY(cell.richText());
QVERIFY(cell.userInput().split('\n').count() == 1);
}
{ // The text:line-break should be translated into a \n newline and since there is no other rich-text it should not be detected as such.
KoXmlDocument doc = xmlDocument("<text:p>First<text:line-break/>Second</text:p>");
KoXmlElement e = doc.documentElement();
QVERIFY(!e.isNull());
cell.loadOdfCellText(e, context, autoStyles, cellStyleName);
QVERIFY(!cell.isNull());
QVERIFY(!cell.richText());
QVERIFY(cell.userInput().split('\n').count() == 2);
}
{ // The text:s and text:tab should be translated into space and tabulator. No rich-text else.
KoXmlDocument doc = xmlDocument("<text:p>First<text:s/>Second<text:tab/>Theird</text:p>");
KoXmlElement e = doc.documentElement();
QVERIFY(!e.isNull());
cell.loadOdfCellText(e, context, autoStyles, cellStyleName);
QVERIFY(!cell.isNull());
QVERIFY(!cell.richText());
QVERIFY(cell.userInput().split('\n').count() == 1);
}
}
-QTEST_KDEMAIN(CellTest, GUI)
+QTEST_MAIN(CellTest)
diff --git a/sheets/tests/TestCell.h b/sheets/tests/TestCell.h
index f1db91e5a4a..aa631e0e0d3 100644
--- a/sheets/tests/TestCell.h
+++ b/sheets/tests/TestCell.h
@@ -1,45 +1,46 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
Copyright 2011 Sebastian Sauer <sebastian.sauer@kdab.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_CELL_TEST
#define CALLIGRA_SHEETS_CELL_TEST
-#include <QtTest>
+#include <QObject>
class KoXmlDocument;
+class QString;
namespace Calligra
{
namespace Sheets
{
class CellTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testRichText();
private:
KoXmlDocument xmlDocument(const QString &content);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_CELL_STORAGE_TEST
diff --git a/sheets/tests/TestCellStorage.cpp b/sheets/tests/TestCellStorage.cpp
index 390442975bc..5666a6944d1 100644
--- a/sheets/tests/TestCellStorage.cpp
+++ b/sheets/tests/TestCellStorage.cpp
@@ -1,75 +1,75 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestCellStorage.h"
#include <sheets/CellStorage.h>
#include <sheets/Map.h>
#include <sheets/Sheet.h>
#include <sheets/Value.h>
-#include <qtest_kde.h>
+#include <QTest>
using namespace Calligra::Sheets;
void CellStorageTest::testMergedCellsInsertRowBug()
{
Map map;
Sheet* sheet = map.addNewSheet();
CellStorage* storage = sheet->cellStorage();
// | 1 | 4 |
// |-------|
// | 2 |
// |-------|
// | | 5 |
// | 3 |---|
// | | 6 |
// |-------|
// | 7 | 8 |
storage->setValue(1, 1, Value(1));
storage->setValue(2, 1, Value(4));
storage->setValue(1, 2, Value(2));
storage->setValue(1, 3, Value(3));
storage->setValue(2, 3, Value(5));
storage->setValue(2, 4, Value(6));
storage->setValue(1, 5, Value(7));
storage->setValue(2, 5, Value(8));
storage->mergeCells(1, 2, 2, 1);
storage->mergeCells(1, 3, 1, 2);
// insert a row
storage->insertRows(5, 1);
// validate result
QCOMPARE(storage->value(1, 1), Value(1));
QCOMPARE(storage->value(2, 1), Value(4));
QCOMPARE(storage->value(1, 2), Value(2));
QCOMPARE(storage->value(1, 3), Value(3));
QCOMPARE(storage->value(2, 3), Value(5));
QCOMPARE(storage->value(2, 4), Value(6));
QCOMPARE(storage->value(1, 6), Value(7));
QCOMPARE(storage->value(2, 6), Value(8));
QCOMPARE(storage->mergedXCells(1, 2), 2);
QCOMPARE(storage->mergedYCells(1, 2), 1);
QCOMPARE(storage->mergedXCells(1, 3), 1);
QCOMPARE(storage->mergedYCells(1, 3), 2);
}
-QTEST_KDEMAIN(CellStorageTest, GUI)
+QTEST_MAIN(CellStorageTest)
diff --git a/sheets/tests/TestCellStorage.h b/sheets/tests/TestCellStorage.h
index d9cfe99fbcc..be44e989cf6 100644
--- a/sheets/tests/TestCellStorage.h
+++ b/sheets/tests/TestCellStorage.h
@@ -1,40 +1,40 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_CELL_STORAGE_TEST
#define CALLIGRA_SHEETS_CELL_STORAGE_TEST
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class CellStorageTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testMergedCellsInsertRowBug();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_CELL_STORAGE_TEST
diff --git a/sheets/tests/TestDatabaseFilter.cpp b/sheets/tests/TestDatabaseFilter.cpp
index 8fa35dac40f..6c59dac3818 100644
--- a/sheets/tests/TestDatabaseFilter.cpp
+++ b/sheets/tests/TestDatabaseFilter.cpp
@@ -1,90 +1,90 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestDatabaseFilter.h"
#include <sheets/database/Filter.h>
-#include <qtest_kde.h>
+#include <QTest>
using namespace Calligra::Sheets;
void DatabaseFilterTest::testIsEmpty()
{
Filter f;
QVERIFY(f.isEmpty());
}
void DatabaseFilterTest::testEmptyEquals()
{
Filter a, b;
QVERIFY2(a == b, "Two empty filters are equal");
}
void DatabaseFilterTest::testSimpleEquals()
{
Filter a;
Filter b;
a.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
QVERIFY(a != b);
b.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
QVERIFY(a == b);
}
void DatabaseFilterTest::testNotEquals1()
{
Filter a;
Filter b;
a.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
b.addCondition(Filter::AndComposition, 0, Filter::Match, "test2");
QVERIFY(a != b);
}
void DatabaseFilterTest::testNotEquals2()
{
Filter a;
Filter b;
a.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
b.addCondition(Filter::AndComposition, 0, Filter::NotMatch, "test");
QVERIFY(a != b);
}
void DatabaseFilterTest::testAndEquals()
{
Filter a;
Filter b;
a.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
b.addCondition(Filter::AndComposition, 0, Filter::Match, "test");
a.addCondition(Filter::AndComposition, 0, Filter::Match, "test2");
b.addCondition(Filter::AndComposition, 0, Filter::Match, "test2");
QVERIFY(a == b);
}
void DatabaseFilterTest::testOrEquals()
{
Filter a;
Filter b;
a.addCondition(Filter::OrComposition, 0, Filter::Match, "test");
b.addCondition(Filter::OrComposition, 0, Filter::Match, "test");
a.addCondition(Filter::OrComposition, 0, Filter::Match, "test2");
b.addCondition(Filter::OrComposition, 0, Filter::Match, "test2");
QVERIFY(a == b);
}
-QTEST_KDEMAIN(DatabaseFilterTest, GUI)
+QTEST_MAIN(DatabaseFilterTest)
diff --git a/sheets/tests/TestDatabaseFilter.h b/sheets/tests/TestDatabaseFilter.h
index 1d16f1f1cf3..024695ff586 100644
--- a/sheets/tests/TestDatabaseFilter.h
+++ b/sheets/tests/TestDatabaseFilter.h
@@ -1,46 +1,46 @@
/* This file is part of the KDE project
Copyright 2011 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_DATABASE_FILTER_TEST
#define CALLIGRA_SHEETS_DATABASE_FILTER_TEST
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class DatabaseFilterTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testIsEmpty();
void testEmptyEquals();
void testSimpleEquals();
void testNotEquals1();
void testNotEquals2();
void testAndEquals();
void testOrEquals();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_DATABASE_FILTER_TEST
diff --git a/sheets/tests/TestDatabaseFunctions.cpp b/sheets/tests/TestDatabaseFunctions.cpp
index 5aa99b7564c..30c8064c4ef 100644
--- a/sheets/tests/TestDatabaseFunctions.cpp
+++ b/sheets/tests/TestDatabaseFunctions.cpp
@@ -1,161 +1,161 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestDatabaseFunctions.h"
#include "CellStorage.h"
#include "Map.h"
#include "NamedAreaManager.h"
#include "Sheet.h"
#include "TestKspreadCommon.h"
#define CHECK_EVAL(x,y) { Value z(RoundNumber(y)); QCOMPARE(evaluate(x,z), (z)); }
#define ROUND(x) (roundf(1e10 * x) / 1e10)
// round to get at most 10-digits number
static Value RoundNumber(const Value& v)
{
if (v.isNumber()) {
double d = numToDouble(v.asFloat());
if (fabs(d) < DBL_EPSILON)
d = 0.0;
return Value(ROUND(d));
} else
return v;
}
Value TestDatabaseFunctions::evaluate(const QString& formula, Value& ex)
{
Formula f(m_map->sheet(0));
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return RoundNumber(result);
}
void TestDatabaseFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
m_map = new Map(0 /* no Doc */);
m_map->addNewSheet();
Sheet* sheet = m_map->sheet(0);
CellStorage* storage = sheet->cellStorage();
// TESTDB = A18:I31
m_map->namedAreaManager()->insert(Region(QRect(QPoint(1, 18), QPoint(9, 31)), sheet), "TESTDB");
// A18:A31
storage->setValue(1, 18, Value("TestID"));
for (int row = 19; row <= 31; ++row)
storage->setValue(1, row, Value((double)::pow(2.0, row - 19)));
// B18:B31
storage->setValue(2, 18, Value("Constellation"));
QList<QString> constellations = QList<QString>() << "Cancer" << "Canis Major" << "Canis Minor"
<< "Carina" << "Draco" << "Eridanus" << "Gemini" << "Hercules" << "Orion" << "Phoenix"
<< "Scorpio" << "Ursa Major" << "Ursa Minor";
for (int i = 0; i < constellations.count(); ++i)
storage->setValue(2, 19 + i, Value(constellations[i]));
// C18:C31
storage->setValue(3, 18, Value("Bright Stars"));
QList<int> stars = QList<int>() << 0 << 5 << 2 << 5 << 3 << 4 << 4 << 0 << 8 << 1 << 9 << 6 << 2;
for (int i = 0; i < stars.count(); ++i)
storage->setValue(3, 19 + i, Value(stars[i]));
// B36:B37
storage->setValue(2, 36, Value("Bright Stars"));
storage->setValue(2, 37, Value(4));
// D36:D37
storage->setValue(4, 36, Value("Constellation"));
storage->setValue(4, 37, Value("Ursa Major"));
}
void TestDatabaseFunctions::testDAVERAGE()
{
CHECK_EVAL("=DAVERAGE(TESTDB; \"TestID\"; B36:B37)", Value(48));
}
void TestDatabaseFunctions::testDCOUNT()
{
CHECK_EVAL("=DCOUNT(TESTDB; \"Bright Stars\"; B36:B37)", Value(2));
}
void TestDatabaseFunctions::testDCOUNTA()
{
CHECK_EVAL("=DCOUNTA(TESTDB; \"Bright Stars\"; B36:B37)", Value(2));
}
void TestDatabaseFunctions::testDGET()
{
CHECK_EVAL("=DGET(TESTDB; \"TestID\"; D36:D37)", Value(2048));
CHECK_EVAL("=DGET(TESTDB; \"TestID\"; B36:B37)", Value::errorVALUE());
}
void TestDatabaseFunctions::testDMAX()
{
CHECK_EVAL("=DMAX(TESTDB; \"TestID\"; B36:B37)", Value(64));
}
void TestDatabaseFunctions::testDMIN()
{
CHECK_EVAL("=DMIN(TESTDB; \"TestID\"; B36:B37)", Value(32));
}
void TestDatabaseFunctions::testDPRODUCT()
{
CHECK_EVAL("=DPRODUCT(TESTDB; \"TestID\"; B36:B37)", Value(2048));
}
void TestDatabaseFunctions::testDSTDEV()
{
CHECK_EVAL("=DSTDEV(TESTDB; \"TestID\"; B36:B37)", Value(22.6274169979695));
}
void TestDatabaseFunctions::testDSTDEVP()
{
CHECK_EVAL("=DSTDEVP(TESTDB; \"TestID\"; B36:B37)", Value(16));
}
void TestDatabaseFunctions::testDSUM()
{
CHECK_EVAL("=DSUM(TESTDB; \"TestID\"; B36:B37)", Value(96));
}
void TestDatabaseFunctions::testDVAR()
{
CHECK_EVAL("=DVAR(TESTDB; \"TestID\"; B36:B37)", Value(512));
}
void TestDatabaseFunctions::testDVARP()
{
CHECK_EVAL("=DVARP(TESTDB; \"TestID\"; B36:B37)", Value(256));
}
void TestDatabaseFunctions::cleanupTestCase()
{
delete m_map;
}
-QTEST_KDEMAIN(TestDatabaseFunctions, GUI)
+QTEST_MAIN(TestDatabaseFunctions)
diff --git a/sheets/tests/TestDatabaseFunctions.h b/sheets/tests/TestDatabaseFunctions.h
index 840ea74e90d..52986873993 100644
--- a/sheets/tests/TestDatabaseFunctions.h
+++ b/sheets/tests/TestDatabaseFunctions.h
@@ -1,63 +1,62 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_DATABASE_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_DATABASE_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class Map;
class TestDatabaseFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testDAVERAGE();
void testDCOUNT();
void testDCOUNTA();
void testDGET();
void testDMAX();
void testDMIN();
void testDPRODUCT();
void testDSTDEV();
void testDSTDEVP();
void testDSUM();
void testDVAR();
void testDVARP();
void cleanupTestCase();
private:
Value evaluate(const QString&, Value& ex);
Map* m_map;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_DATABASE_FUNCTIONS
diff --git a/sheets/tests/TestDatetimeFunctions.cpp b/sheets/tests/TestDatetimeFunctions.cpp
index c452941c3ac..5145ebc6ca7 100644
--- a/sheets/tests/TestDatetimeFunctions.cpp
+++ b/sheets/tests/TestDatetimeFunctions.cpp
@@ -1,562 +1,562 @@
/* This file is part of the KDE project
Copyright 2007 Sascha Pfau <MrPeacock@web.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestDatetimeFunctions.h"
#include "TestKspreadCommon.h"
void TestDatetimeFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
#define CHECK_EVAL(x,y) { Value z(RoundNumber(y)); QCOMPARE(evaluate(x,z), (z)); }
#define CHECK_FAIL(x,y,txt) { Value z(RoundNumber(y)); QEXPECT_FAIL("", txt, Continue); QCOMPARE(evaluate(x,z), (z));}
#define ROUND(x) (roundf(1e10 * x) / 1e10)
// changelog
/////////////////////////////////////
// 18.05.07
// - fix typo in yearfrac
// - indend
// - added missing tests EOMONTH()
// - added missing values in DATEDIF
// 02.06.07
// - added Isoweeknum tests starts on sunday
// - added WEEKINYEAR unittests
// - added ISLEAPYEAR unittests
// - added DAYSINMONTH unittests
// 15.07.07
// - modified YEARFRAC basis=1
// 30.10.07
// - fixed WEEKNUM tests
// - corrected wrong DAYS360,EDATE and EOMONTH unittests
// - commented out last issue on YEARFRAC
#if 0 // not used?
// round to get at most 10-digits number
static Value RoundNumber(double f)
{
return Value(ROUND(f));
}
#endif
// round to get at most 10-digits number
static Value RoundNumber(const Value& v)
{
if (v.isNumber()) {
double d = numToDouble(v.asFloat());
if (fabs(d) < DBL_EPSILON)
d = 0.0;
return Value(ROUND(d));
} else
return v;
}
Value TestDatetimeFunctions::evaluate(const QString& formula, Value& ex)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return RoundNumber(result);
}
void TestDatetimeFunctions::testYEARFRAC()
{
// basis 0 US
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-06-30\" ; 0)", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-07-01\" ; 0)", Value(0.5000000000));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2000-06-30\" ; 0)", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"2000-01-15\" ; \"2000-09-17\" ; 0)", Value(0.6722222222));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\" ; 0)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-01-01\" ; \"2002-01-01\" ; 0)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\" ; 0)", Value(0.0694444444));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\" ; 0)", Value(6.5138888889));
// basis 0 is default
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-06-30\")", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-07-01\")", Value(0.5000000000));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2000-06-30\")", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"2000-01-15\" ; \"2000-09-17\")", Value(0.6722222222));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\")", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-01-01\" ; \"2002-01-01\")", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\")", Value(0.0694444444));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\")", Value(6.5138888889));
// basis 1 Actual/actual
// other values are taken from OOo-2.2.1
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-06-30\" ; 1)", Value(0.4931506849));
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-07-01\" ; 1)", Value(0.4958904110));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2000-06-30\" ; 1)", Value(0.4945355191));
CHECK_EVAL("YEARFRAC( \"2000-01-15\" ; \"2000-09-17\" ; 1)", Value(0.6721311475));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\" ; 1)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\" ; 1)", Value(0.0684931507));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\" ; 1)", Value(6.5099726242)); // specs 6.5099726242 OOo-2.3.0 6.5081967213
CHECK_EVAL("YEARFRAC( \"2003-12-06\" ; \"2004-03-05\" ; 1)", Value(0.2459016393));
CHECK_EVAL("YEARFRAC( \"2003-12-31\" ; \"2004-03-31\" ; 1)", Value(0.2486338798));
CHECK_EVAL("YEARFRAC( \"2004-10-01\" ; \"2005-01-11\" ; 1)", Value(0.2794520548));
CHECK_EVAL("YEARFRAC( \"2004-10-26\" ; \"2005-02-06\" ; 1)", Value(0.2821917808));
CHECK_EVAL("YEARFRAC( \"2004-11-20\" ; \"2005-03-04\" ; 1)", Value(0.2849315068));
CHECK_EVAL("YEARFRAC( \"2004-12-15\" ; \"2005-03-30\" ; 1)", Value(0.2876712329));
CHECK_EVAL("YEARFRAC( \"2000-12-01\" ; \"2001-01-16\" ; 1)", Value(0.1260273973));
CHECK_EVAL("YEARFRAC( \"2000-12-26\" ; \"2001-02-11\" ; 1)", Value(0.1287671233));
// basis 2 Actual/360
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2000-06-30\" ; 2)", Value(0.5027777778));
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-06-30\" ; 2)", Value(0.5000000000));
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-07-01\" ; 2)", Value(0.5027777778));
CHECK_EVAL("YEARFRAC( \"2000-01-15\" ; \"2000-09-17\" ; 2)", Value(0.6833333333));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\" ; 2)", Value(1.0166666667));
CHECK_EVAL("YEARFRAC( \"2001-01-01\" ; \"2002-01-01\" ; 2)", Value(1.0138888889));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\" ; 2)", Value(0.0694444444));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\" ; 2)", Value(6.6055555556));
// basis 3 Actual/365
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\" ; 3)", Value(1.0027397260));
CHECK_EVAL("YEARFRAC( \"2001-01-01\" ; \"2002-01-01\" ; 3)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\" ; 3)", Value(0.0684931507));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\" ; 3)", Value(6.5150684932));
// basis 4 European 30/360
CHECK_EVAL("YEARFRAC( \"1999-01-01\" ; \"1999-06-30\" ; 4)", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2000-06-30\" ; 4)", Value(0.4972222222));
CHECK_EVAL("YEARFRAC( \"2000-01-15\" ; \"2000-09-17\" ; 4)", Value(0.6722222222));
CHECK_EVAL("YEARFRAC( \"2000-01-01\" ; \"2001-01-01\" ; 4)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-01-01\" ; \"2002-01-01\" ; 4)", Value(1.0000000000));
CHECK_EVAL("YEARFRAC( \"2001-12-05\" ; \"2001-12-30\" ; 4)", Value(0.0694444444));
CHECK_EVAL("YEARFRAC( \"2000-02-05\" ; \"2006-08-10\" ; 4)", Value(6.5138888889));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYEARFRAC(\"1999-01-01\";\"1999-06-30\";1)", Value(0.4931506849));
}
void TestDatetimeFunctions::testDATEDIF()
{
// interval y ( years )
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"y\")", Value(3)); // TODO check value; kspread says 3
// interval m ( Months. If there is not a complete month between the dates, 0 will be returned.)
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"m\")", Value(43));
// interval d ( Days )
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"d\")", Value(1308)); // TODO check value; kspread says 1308
// interval md ( Days, ignoring months and years )
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"md\")", Value(0));
// interval ym ( Months, ignoring years )
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"ym\")", Value(7));
// interval yd ( Days, ignoring years )
CHECK_EVAL("DATEDIF(DATE(1990;2;15); DATE(1993;9;15); \"yd\")", Value(212)); // TODO check value; kspread says 212
}
void TestDatetimeFunctions::testISLEAPYEAR()
{
// only every 400 years ...
CHECK_EVAL("ISLEAPYEAR(1900)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2000)", Value(true));
CHECK_EVAL("ISLEAPYEAR(2100)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2200)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2300)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2400)", Value(true));
CHECK_EVAL("ISLEAPYEAR(1900)", Value(false));
// and every 4th year
CHECK_EVAL("ISLEAPYEAR(2000)", Value(true));
CHECK_EVAL("ISLEAPYEAR(2001)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2002)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2003)", Value(false));
CHECK_EVAL("ISLEAPYEAR(2004)", Value(true));
// test alternate name for the ISLEAPYEAR function
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR(1900)", Value(false));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR(2000)", Value(true));
}
void TestDatetimeFunctions::testWEEKNUM()
{
// is known as weeknum_add() in OOo
// type default ( type 1 )
CHECK_EVAL("WEEKNUM(DATE(2000;05;21))", Value(22)); //
CHECK_EVAL("WEEKNUM(DATE(2005;01;01))", Value(01)); //
CHECK_EVAL("WEEKNUM(DATE(2000;01;02))", Value(02)); //
CHECK_EVAL("WEEKNUM(DATE(2000;01;03))", Value(02)); //
CHECK_EVAL("WEEKNUM(DATE(2000;01;04))", Value(02)); //
CHECK_EVAL("WEEKNUM(DATE(2006;01;01))", Value(01)); //
// type 1
CHECK_EVAL("WEEKNUM(DATE(2000;05;21);1)", Value(22));
CHECK_EVAL("WEEKNUM(DATE(2008;03;09);1)", Value(11));
// type 2
CHECK_EVAL("WEEKNUM(DATE(2000;05;21);2)", Value(21));
CHECK_EVAL("WEEKNUM(DATE(2005;01;01);2)", Value(01)); // ref. OOo-2.2.0 = 1
CHECK_EVAL("WEEKNUM(DATE(2000;01;02);2)", Value(01)); // ref. OOo-2.2.0 = 1
CHECK_EVAL("WEEKNUM(DATE(2000;01;03);2)", Value(02)); // ref. OOo-2.2.0 = 2
CHECK_EVAL("WEEKNUM(DATE(2000;01;04);2)", Value(02)); // ref. OOo-2.2.0 = 2
CHECK_EVAL("WEEKNUM(DATE(2008;03;09);2)", Value(10));
// additional tests for method 2
CHECK_EVAL("WEEKNUM(DATE(2006;01;01);2)", Value(01));
CHECK_EVAL("WEEKNUM(DATE(2006;01;02);2)", Value(02));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWEEKNUM(DATE(2000;05;21);1)", Value(22));
}
void TestDatetimeFunctions::testWEEKSINYEAR()
{
//
CHECK_EVAL("WEEKSINYEAR(1995)", Value(52));
CHECK_EVAL("WEEKSINYEAR(1992)", Value(53));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETWEEKSINYEAR(1992)", Value(53));
}
void TestDatetimeFunctions::testWORKDAY()
{
// 2001 JAN 01 02 03 04 05 06 07 08
// MO TU WE TH FR SA SU MO
// 01 02 -- --
CHECK_EVAL("WORKDAY(DATE(2001;01;01);2;2)=DATE(2001;01;05)", Value(true));
CHECK_EVAL("WORKDAY(DATE(2001;01;01);2;3)=DATE(2001;01;08)", Value(true));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWORKDAY(DATE(2001;01;01);2;3)=DATE(2001;01;08)", Value(true));
}
void TestDatetimeFunctions::testNETWORKDAY()
{
// 2001 JAN 01 02 03 04 05 06 07 08 09
// MO TU WE TH FR SA SU MO TU
// 01 02 03 04 05 05 05 06 ... networkdays
CHECK_EVAL("NETWORKDAY(DATE(2001;01;01);DATE(2001;01;08))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2001;01;01);DATE(2001;01;07))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2001;01;01);DATE(2001;01;06))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2001;01;01);DATE(2001;01;05))", Value(4));
// 2008 FEB 25 26 27 28 29 01 02 03 04
// MO TU WE TH FR SA SU MO TU
// 01 02 03 04 05 05 05 06 ... networkdays
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;02;28))", Value(3));
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;02;29))", Value(4));
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;03;01))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;03;02))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;03;03))", Value(5));
CHECK_EVAL("NETWORKDAY(DATE(2008;02;25);DATE(2008;03;04))", Value(6));
}
void TestDatetimeFunctions::testUNIX2DATE()
{
// 01/01/2001 = 946684800
CHECK_EVAL("UNIX2DATE(946684800)=DATE(2000;01;01)", Value(true)); // TODO result of various unix-timestamp calculator is 946681200 (UTC?)
}
void TestDatetimeFunctions::testDATE2UNIX()
{
// 946681200 = 01/01/2001
CHECK_EVAL("DATE2UNIX(DATE(2000;01;01))=946684800", Value(true)); // TODO
}
void TestDatetimeFunctions::testDATE()
{
//
CHECK_EVAL("DATE(2005;12;31)-DATE(1904;01;01)", Value(37255));
CHECK_EVAL("DATE(2004;02;29)=DATE(2004;02;28)+1", Value(true)); // leap year
CHECK_EVAL("DATE(2000;02;29)=DATE(2000;02;28)+1", Value(true)); // leap year
CHECK_EVAL("DATE(2005;03;01)=DATE(2005;02;28)+1", Value(true)); // no leap year
CHECK_EVAL("DATE(2017.5;01;02)=DATE(2017;01;02)", Value(true)); // fractional values for year are truncated
CHECK_EVAL("DATE(2006; 2.5; 3)=DATE(2006; 2; 3)", Value(true)); // fractional values for month are truncated
CHECK_EVAL("DATE(2006;01;03.5)=DATE(2006;01;03)", Value(true)); // fractional values for day are truncated
CHECK_EVAL("DATE(2006;13;03)=DATE(2007;01;03)", Value(true)); // months > 12 roll over to year
CHECK_EVAL("DATE(2006;01;32)=DATE(2006;02;01)", Value(true)); // days greater than month limit roll over to month
CHECK_EVAL("DATE(2006;25;34)=DATE(2008;02;03)", Value(true)); // days and months roll over transitively
CHECK_EVAL("DATE(2006;-01;01)=DATE(2005;11;01)", Value(true)); // negative months roll year backward
CHECK_EVAL("DATE(2006;04;-01)=DATE(2006;03;30)", Value(true)); // negative days roll month backward
CHECK_EVAL("DATE(2006;-4;-1)=DATE(2005;07;30)", Value(true)); // negative days and months roll backward transitively
CHECK_EVAL("DATE(2003;2;29)=DATE(2003;03;01)", Value(true)); // non-leap year rolls forward
}
void TestDatetimeFunctions::testDATEVALUE()
{
//
CHECK_EVAL("DATEVALUE(\"2004-12-25\")=DATE(2004;12;25)", Value(true));
}
void TestDatetimeFunctions::testDAY()
{
//
CHECK_EVAL("DAY(DATE(2006;05;21))", Value(21));
CHECK_EVAL("DAY(\"2006-12-15\")", Value(15));
}
void TestDatetimeFunctions::testDAYS()
{
//
CHECK_EVAL("DAYS(DATE(1993;4;16); DATE(1993;9;25))", Value(-162)); //
}
void TestDatetimeFunctions::testDAYSINMONTH()
{
// non leapyear
CHECK_EVAL("DAYSINMONTH(1995;01)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;02)", Value(28));
CHECK_EVAL("DAYSINMONTH(1995;03)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;04)", Value(30));
CHECK_EVAL("DAYSINMONTH(1995;05)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;06)", Value(30));
CHECK_EVAL("DAYSINMONTH(1995;07)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;08)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;09)", Value(30));
CHECK_EVAL("DAYSINMONTH(1995;10)", Value(31));
CHECK_EVAL("DAYSINMONTH(1995;11)", Value(30));
CHECK_EVAL("DAYSINMONTH(1995;12)", Value(31));
// leapyear
CHECK_EVAL("DAYSINMONTH(2000;02)", Value(29));
CHECK_EVAL("DAYSINMONTH(1900;02)", Value(28)); // non leapyear
CHECK_EVAL("DAYSINMONTH(2004;02)", Value(29));
// test alternate name for the DAYSINMONTH function
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINMONTH(1995;01)", Value(31)); // alternate function name
}
void TestDatetimeFunctions::testDAYSINYEAR()
{
CHECK_EVAL("DAYSINYEAR(2000)", Value(366));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINYEAR(2000)", Value(366)); // alternate function name
}
void TestDatetimeFunctions::testDAYS360()
{
// TODO Note: Lotus 1-2-3v9.8 has a function named DAYS but with different semantics. It supports an optional "Basis" parameter
// with many different options. Without the optional parameter, it defaults to a 30/360 basis, not calendar days; thus, in Lotus 1-2-3v9.8,
// DAYS(DATE(1993;4;16); DATE(1993;9;25)) computes -159, not -162.
CHECK_EVAL("DAYS360(DATE(1993;4;16);DATE(1993;9;25); FALSE)", Value(159)); // specs. -162 but OOo and KSpread calculate 159
CHECK_EVAL("DAYS360(\"2002-02-22\"; \"2002-04-21\" ; FALSE)", Value(59)); // ref. docs
}
void TestDatetimeFunctions::testEDATE()
{
//
CHECK_EVAL("EDATE(\"2006-01-01\";0) =DATE(2006;01;01)", Value(true)); // If zero, unchanged.
CHECK_EVAL("EDATE(DATE(2006;01;01);0)=DATE(2006;01;01)", Value(true)); // You can pass strings or serial numbers to EDATE
CHECK_EVAL("EDATE(\"2006-01-01\"; 2) =DATE(2006;03;01)", Value(true)); //
CHECK_EVAL("EDATE(\"2006-01-01\";-2) =DATE(2005;11;01)", Value(true)); // 2006 is not a leap year. Last day of March, going back to February
CHECK_EVAL("EDATE(\"2000-04-30\";-2) =DATE(2000; 2;29)", Value(true)); // TODO 2000 was a leap year, so the end of February is the 29th
CHECK_EVAL("EDATE(\"2000-04-05\";24 )=DATE(2002;04;05)", Value(true)); // EDATE isn't limited to 12 months
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEDATE(\"2006-01-01\";0) =DATE(2006;01;01)", Value(true)); // alternate function name
}
void TestDatetimeFunctions::testEOMONTH()
{
//
CHECK_EVAL("EOMONTH(\"2006-01-01\";0) =DATE(2006;01;31)", Value(true)); // If zero, unchanged V just returns
// end of that date's month. (January in this case)
CHECK_EVAL("EOMONTH(DATE(2006;01;01);0)=DATE(2006;01;31)", Value(true)); // You can pass strings or serial numbers to EOMONTH
CHECK_EVAL("EOMONTH(\"2006-01-01\";2) =DATE(2006;03;31)", Value(true)); // End of month of March is March 31.
CHECK_EVAL("EOMONTH(\"2006-01-01\";-2) =DATE(2005;11;30)", Value(true)); // Nov. 30 is the last day of November
CHECK_EVAL("EOMONTH(\"2006-03-31\";-1) =DATE(2006;02;28)", Value(true)); // 2006 is not a leap year. Last day of February is Feb. 28.
CHECK_EVAL("EOMONTH(\"2000-04-30\";-2) =DATE(2000;02;29)", Value(true)); // 2000 was a leap year, so the end of February is the 29th
CHECK_EVAL("EOMONTH(\"2000-04-05\";24) =DATE(2002;04;30)", Value(true)); // Not limited to 12 months, and this tests April
CHECK_EVAL("EOMONTH(\"2006-01-05\";04) =DATE(2002;05;31)", Value(false)); // End of May is May 31
CHECK_EVAL("EOMONTH(\"2006-01-05\";05) =DATE(2002;06;30)", Value(false)); // June 30
CHECK_EVAL("EOMONTH(\"2006-01-05\";06) =DATE(2002;07;31)", Value(false)); // July 31
CHECK_EVAL("EOMONTH(\"2006-01-05\";07) =DATE(2002;08;31)", Value(false)); // August 31
CHECK_EVAL("EOMONTH(\"2006-01-05\";08) =DATE(2002;09;30)", Value(false)); // Sep 30
CHECK_EVAL("EOMONTH(\"2006-01-05\";09) =DATE(2002;10;31)", Value(false)); // Oct 31
CHECK_EVAL("EOMONTH(\"2006-01-05\";10) =DATE(2002;11;30)", Value(false)); // Nov. 30
CHECK_EVAL("EOMONTH(\"2006-01-05\";11) =DATE(2002;12;31)", Value(false)); // Dec. 31
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEOMONTH(\"2006-01-01\";0) =DATE(2006;01;31)", Value(true)); // alternate function name
}
void TestDatetimeFunctions::testHOUR()
{
//
CHECK_EVAL("HOUR(5/24)", Value(5)); // 5/24ths of a day is 5 hours, aka 5AM.
CHECK_EVAL("HOUR(5/24-1/(24*60*60))", Value(4)); // A second before 5AM, it's 4AM.
CHECK_EVAL("HOUR(\"14:00\")", Value(14)); // TimeParam accepts text
CHECK_EVAL("HOUR(\"9:00\")", Value(9));
CHECK_EVAL("HOUR(\"09:00\")", Value(9));
CHECK_EVAL("HOUR(\"23:00\")", Value(23));
CHECK_EVAL("HOUR(\"11:00 PM\")", Value(23));
CHECK_EVAL("HOUR(\"11:00 AM\")", Value(11));
}
void TestDatetimeFunctions::testISOWEEKNUM()
{
// ODF-tests
CHECK_EVAL("ISOWEEKNUM(DATE(1995;1;1);1)", Value(1)); // January 1, 1995 was a Sunday
CHECK_EVAL("ISOWEEKNUM(DATE(1995;1;1);2)", Value(52)); // January 1, 1995 was a Sunday, so if Monday is the beginning of the week,
// then it's week 52 of the previous year
CHECK_EVAL("ISOWEEKNUM(DATE(1995;1;1))", Value(52)); // Default is Monday is beginning of week (per ISO)
CHECK_EVAL("ISOWEEKNUM(DATE(2000;5;21))", Value(20)); // ref OOo-2.2.0
CHECK_EVAL("ISOWEEKNUM(DATE(2000;5;21);1)", Value(21)); // ref OOo-2.2.0
CHECK_EVAL("ISOWEEKNUM(DATE(2000;5;21);2)", Value(20)); // ref OOo-2.2.0
CHECK_EVAL("ISOWEEKNUM(DATE(2005;1;1))", Value(53)); // ref OOo-2.2.0
CHECK_EVAL("ISOWEEKNUM(DATE(2005;1;2))", Value(53)); // ref OOo-2.2.0
CHECK_EVAL("ISOWEEKNUM(DATE(2006;1;1))", Value(52)); // ref OOo-2.2.0
// method 2 - week begins on sunday
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;01);2)", Value(52)); // January 1, 1995 was a Sunday
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;02);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;03);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;04);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;05);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;06);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;07);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;08);2)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;09);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;10);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;11);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;12);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;13);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;14);2)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;15);2)", Value(2)); //
// method 1 - week begins on monday
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;01);1)", Value(1)); // January 1, 1995 was a Sunday
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;02);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;03);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;04);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;05);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;06);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;07);1)", Value(1)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;08);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;09);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;10);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;11);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;12);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;13);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;14);1)", Value(2)); //
CHECK_EVAL("ISOWEEKNUM(DATE(1995;01;15);1)", Value(3)); //
}
void TestDatetimeFunctions::testMINUTE()
{
//
CHECK_EVAL("MINUTE(1/(24*60))", Value(1)); // 1 minute is 1/(24*60) of a day.
CHECK_EVAL("MINUTE(TODAY()+1/(24*60))", Value(1)); // If you start with today, and add a minute, you get a minute.
CHECK_EVAL("MINUTE(1/24)", Value(0)); // At the beginning of the hour, we have 0 minutes.
}
void TestDatetimeFunctions::testMONTH()
{
//
CHECK_EVAL("MONTH(DATE(2006;5;21))", Value(5)); // Month extraction from DATE() value
}
void TestDatetimeFunctions::testMONTHS()
{
CHECK_EVAL("MONTHS(\"2002-01-18\"; \"2002-02-26\"; 0)", Value(1));
CHECK_EVAL("MONTHS(\"2002-01-19\"; \"2002-02-26\"; 1)", Value(0));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS(\"2002-01-18\"; \"2002-02-26\"; 0)", Value(1));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS(\"2002-01-19\"; \"2002-02-26\"; 1)", Value(0));
}
void TestDatetimeFunctions::testNOW()
{
//
CHECK_EVAL("NOW()>DATE(2006;1;3)", Value(true)); // NOW constantly changes, but we know it's beyond this date.
CHECK_EVAL("INT(NOW())=TODAY()", Value(true));
}
void TestDatetimeFunctions::testSECOND()
{
//
CHECK_EVAL("SECOND(1/(24*60*60))", Value(1)); // This is one second into today.
CHECK_EVAL("SECOND(1/(24*60*60*2))", Value(1)); // Rounds.
CHECK_EVAL("SECOND(1/(24*60*60*4))", Value(0)); // Rounds.
}
void TestDatetimeFunctions::testTIME()
{
//
CHECK_EVAL("TIME(0;0;0)", Value(0)); // All zero arguments becomes midnight, 12:00:00 AM.
CHECK_EVAL("TIME(23;59;59)*60*60*24", Value(86399)); // This is 11:59:59 PM.
CHECK_EVAL("TIME(11;125;144)*60*60*24", Value(47244)); // Seconds and minutes roll over transitively; this is 1:07:24 PM.
CHECK_EVAL("TIME(11;0; -117)*60*60*24", Value(39483)); // Negative seconds roll minutes backwards, 10:58:03 AM
CHECK_EVAL("TIME(11;-117;0)*60*60*24", Value(32580)); // Negative minutes roll hours backwards, 9:03:00 AM
CHECK_EVAL("TIME(11;-125;-144)*60*60*24", Value(31956)); // Negative seconds and minutes roll backwards transitively, 8:52:36 AM
// WARNING specs says -31956, but calc and kspread calculate 31956
}
void TestDatetimeFunctions::testTIMEVALUE()
{
//
CHECK_EVAL("TIMEVALUE(\"06:05\") =TIME(6;5;0)", Value(true));
CHECK_EVAL("TIMEVALUE(\"06:05:07\")=TIME(6;5;7)", Value(true));
}
void TestDatetimeFunctions::testTODAY()
{
//
CHECK_EVAL("TODAY()>DATE(2006;1;3)", Value(true)); // Every date TODAY() changes, but we know it's beyond this date.
CHECK_EVAL("INT(TODAY())=TODAY()", Value(true));
}
void TestDatetimeFunctions::testWEEKDAY()
{
// | type 1 | type 2 | type 3
// ---+--------+---------+--------
// 01 | SU | MO | TU
// 02 | MO | TU | WE
// 03 | TU | WE | TH
// 04 | WE | TH | FR
// 05 | TH | FR | SA
// 06 | FR | SA | SU
// 07 | SA | SU | MO
CHECK_EVAL("WEEKDAY(DATE(2006;05;21))", Value(1)); // Year-month-date format
CHECK_EVAL("WEEKDAY(DATE(2005;01;01))", Value(7)); // Saturday
CHECK_EVAL("WEEKDAY(DATE(2005;01;01);1)", Value(7)); // Saturday
CHECK_EVAL("WEEKDAY(DATE(2005;01;01);2)", Value(6)); // Saturday
CHECK_EVAL("WEEKDAY(DATE(2005;01;01);3)", Value(5)); // Saturday
}
void TestDatetimeFunctions::testYEAR()
{
CHECK_EVAL("YEAR(DATE(1904;1;1))", Value(1904));
CHECK_EVAL("YEAR(DATE(2004;1;1))", Value(2004));
}
void TestDatetimeFunctions::testYEARS()
{
CHECK_EVAL("YEARS(\"2001-02-19\"; \"2002-02-26\"; 0)", Value(1));
CHECK_EVAL("YEARS(\"2002-02-19\"; \"2002-02-26\"; 1)", Value(0));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS(\"2001-02-19\";\"2002-02-26\";0)", Value(1));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS(\"2002-02-19\";\"2002-02-26\";1)", Value(0));
}
void TestDatetimeFunctions::testWEEKS()
{
CHECK_EVAL("WEEKS(\"2002-02-18\"; \"2002-02-26\"; 0)", Value(1));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFWEEKS(\"2002-02-18\"; \"2002-02-26\"; 0)", Value(1));
}
-QTEST_KDEMAIN(TestDatetimeFunctions, GUI)
+QTEST_MAIN(TestDatetimeFunctions)
diff --git a/sheets/tests/TestDatetimeFunctions.h b/sheets/tests/TestDatetimeFunctions.h
index b2150776cea..345ff8cd8fc 100644
--- a/sheets/tests/TestDatetimeFunctions.h
+++ b/sheets/tests/TestDatetimeFunctions.h
@@ -1,79 +1,78 @@
/* This file is part of the KDE project
Copyright 2007 Sascha Pfau <MrPeacock@web.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_DATETIME_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_DATETIME_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestDatetimeFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testYEARFRAC();
void testDATEDIF();
void testISLEAPYEAR();
void testWEEKNUM();
void testWEEKSINYEAR();
void testWORKDAY();
void testNETWORKDAY();
void testUNIX2DATE();
void testDATE2UNIX();
void testDATE();
void testDATEVALUE();
void testDAY();
void testDAYS();
void testDAYSINMONTH();
void testDAYSINYEAR();
void testDAYS360();
void testEDATE();
void testEOMONTH();
void testHOUR();
void testISOWEEKNUM();
void testMINUTE();
void testMONTH();
void testMONTHS();
void testNOW();
void testSECOND();
void testTIME();
void testTIMEVALUE();
void testTODAY();
void testWEEKDAY();
void testYEAR();
void testYEARS();
void testWEEKS();
private:
Value evaluate(const QString&, Value& ex);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_DATETIME_FUNCTIONS
diff --git a/sheets/tests/TestDependencies.cpp b/sheets/tests/TestDependencies.cpp
index b37554008bc..d8325173d1e 100644
--- a/sheets/tests/TestDependencies.cpp
+++ b/sheets/tests/TestDependencies.cpp
@@ -1,116 +1,116 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestDependencies.h"
-#include "qtest_kde.h"
+#include <QTest>
#include "CellStorage.h"
#include "DependencyManager.h"
#include "DependencyManager_p.h"
#include "Formula.h"
#include "Map.h"
#include "Region.h"
#include "Sheet.h"
#include "Value.h"
using namespace Calligra::Sheets;
void TestDependencies::initTestCase()
{
m_map = new Map(0 /* no Doc */);
m_sheet = m_map->addNewSheet();
m_sheet->setSheetName("Sheet1");
m_storage = m_sheet->cellStorage();
}
void TestDependencies::testCircleRemoval()
{
Formula formula(m_sheet);
formula.setExpression("=A1");
m_storage->setFormula(1, 1, formula); // A1
QApplication::processEvents(); // handle Damages
QCOMPARE(m_storage->value(1, 1), Value::errorCIRCLE());
DependencyManager* manager = m_map->dependencyManager();
QVERIFY(manager->d->consumers.count() == 1);
QVERIFY(manager->d->providers.count() == 1);
QList<Cell> consumers = manager->d->consumers.value(m_sheet)->contains(QRect(1, 1, 1, 1));
QCOMPARE(consumers.count(), 1);
QCOMPARE(consumers.first(), Cell(m_sheet, 1, 1));
QCOMPARE(manager->d->providers.value(Cell(m_sheet, 1, 1)), Region(QRect(1, 1, 1, 1), m_sheet));
m_storage->setFormula(1, 1, Formula()); // A1
QApplication::processEvents(); // handle Damages
QCOMPARE(m_storage->value(1, 1), Value());
QVERIFY(manager->d->consumers.value(m_sheet)->contains(QRect(1, 1, 1, 1)).count() == 0);
QVERIFY(manager->d->providers.count() == 0);
}
void TestDependencies::testCircles()
{
Formula formula(m_sheet);
formula.setExpression("=A3");
m_storage->setFormula(1, 1, formula); // A1
formula.setExpression("=A1");
m_storage->setFormula(1, 2, formula); // A2
formula.setExpression("=A2");
m_storage->setFormula(1, 3, formula); // A3
QApplication::processEvents(); // handle Damages
QCOMPARE(m_storage->value(1, 1), Value::errorCIRCLE());
QCOMPARE(m_storage->value(1, 2), Value::errorCIRCLE());
QCOMPARE(m_storage->value(1, 3), Value::errorCIRCLE());
}
void TestDependencies::testDepths()
{
Cell a1(m_sheet, 1, 1); a1.setUserInput("5");
Cell a2(m_sheet, 1, 2); a2.setUserInput("=A1");
Cell a3(m_sheet, 1, 3); a3.setUserInput("=A2");
Cell a4(m_sheet, 1, 4); a4.setUserInput("=A1 + A3");
QApplication::processEvents(); // handle Damages
QMap<Cell, int> depths = m_map->dependencyManager()->depths();
QCOMPARE(depths[a1], 0);
QCOMPARE(depths[a2], 1);
QCOMPARE(depths[a3], 2);
QCOMPARE(depths[a4], 3);
a2.setUserInput("");
QApplication::processEvents(); // handle Damages
depths = m_map->dependencyManager()->depths();
QCOMPARE(depths[a1], 0);
QCOMPARE(depths[a2], 0);
QCOMPARE(depths[a3], 1);
QCOMPARE(depths[a4], 2);
}
void TestDependencies::cleanupTestCase()
{
delete m_map;
}
-QTEST_KDEMAIN(TestDependencies, GUI)
+QTEST_MAIN(TestDependencies)
diff --git a/sheets/tests/TestDependencies.h b/sheets/tests/TestDependencies.h
index 5163c328164..3992a4ac9ef 100644
--- a/sheets/tests/TestDependencies.h
+++ b/sheets/tests/TestDependencies.h
@@ -1,54 +1,53 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_DEPENDENCIES
#define CALLIGRA_SHEETS_TEST_DEPENDENCIES
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class CellStorage;
class Map;
class Sheet;
class TestDependencies : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testCircleRemoval();
void testCircles();
void testDepths();
void cleanupTestCase();
private:
CellStorage* m_storage;
Map* m_map;
Sheet* m_sheet;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_DEPENDENCIES
diff --git a/sheets/tests/TestEngineeringFunctions.cpp b/sheets/tests/TestEngineeringFunctions.cpp
index 1ae1c8c589d..c025cddbc32 100644
--- a/sheets/tests/TestEngineeringFunctions.cpp
+++ b/sheets/tests/TestEngineeringFunctions.cpp
@@ -1,396 +1,396 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestEngineeringFunctions.h"
#include "TestKspreadCommon.h"
void TestEngineeringFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
// - different algorithms among spreadsheet programs
// - precision limitation of floating-point number representation
// - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x,y) QCOMPARE(evaluate(x),RoundNumber(y))
#define ROUND(x) (roundf(1e10 * x) / 1e10)
// round to get at most 10-digits number
static Value RoundNumber(double f)
{
return Value(ROUND(f));
}
// round to get at most 10-digits number
static Value RoundNumber(const Value& v)
{
if (v.isComplex()) {
const double imag = numToDouble(v.asComplex().imag());
QString complex = QString::number(ROUND(numToDouble(v.asComplex().real())), 'g', 10);
if (imag >= 0.0)
complex += '+';
complex += QString::number(ROUND(imag), 'g', 10);
complex += 'i';
return Value(complex);
} else if (v.isNumber())
return Value(ROUND(numToDouble(v.asFloat())));
else
return v;
}
Value TestEngineeringFunctions::evaluate(const QString& formula)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
#if 0
// this magically generates the CHECKs
printf(" CHECK_EVAL( \"%s\", %.14e) );\n", qPrintable(formula), result.asFloat());
#endif
return RoundNumber(result);
}
void TestEngineeringFunctions::testBIN2DEC()
{
// ODF-tests
CHECK_EVAL("BIN2DEC(1010)", Value(10));
CHECK_EVAL("BIN2DEC(11111)", Value(31));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2DEC(1010)", Value(10));
}
void TestEngineeringFunctions::testBIN2OCT()
{
// ODF-tests
CHECK_EVAL("BIN2OCT(1010)", Value("12"));
CHECK_EVAL("BIN2OCT(11111)", Value("37"));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2OCT(1010)", Value("12"));
}
void TestEngineeringFunctions::testBIN2HEX()
{
// ODF-tests
CHECK_EVAL("BIN2HEX(1010)", Value("A"));
CHECK_EVAL("BIN2HEX(11111)", Value("1F"));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2HEX(1010)", Value("A"));
}
void TestEngineeringFunctions::testCOMPLEX()
{
CHECK_EVAL("=IMREAL(COMPLEX(1;-3))", 1.0);
CHECK_EVAL("=IMAGINARY(COMPLEX(0;-2))", -2.0);
CHECK_EVAL("=IMAGINARY(COMPLEX(0;-2;\"i\"))", -2.0 );
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOMPLEX(1;-3))", 1.0);
}
void TestEngineeringFunctions::testCONVERT()
{
CHECK_EVAL("=CONVERT(32;\"C\";\"F\")", Value(89.6));
// CHECK_EVAL("=CONVERT(3;\"lbm\";\"kg\")", Value(1.3608));
// CHECK_EVAL("=CONVERT(7.9;\"cal\";\"J\")", Value(33.0757));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCONVERT(32;\"C\";\"F\")", Value(89.6));
}
void TestEngineeringFunctions::testDEC2HEX()
{
CHECK_EVAL("DEC2HEX(12)", Value("C"));
CHECK_EVAL("DEC2HEX(55)", Value("37"));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2HEX(12)", Value("C"));
}
void TestEngineeringFunctions::testDEC2BIN()
{
CHECK_EVAL("DEC2BIN(12)", Value("1100"));
CHECK_EVAL("DEC2BIN(55)", Value("110111"));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2BIN(12)", Value("1100"));
}
void TestEngineeringFunctions::testDEC2OCT()
{
CHECK_EVAL("DEC2OCT(12)", Value("14"));
CHECK_EVAL("DEC2OCT(55)", Value("67"));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2OCT(12)", Value("14"));
}
void TestEngineeringFunctions::testDELTA()
{
CHECK_EVAL("DELTA(1.2; 3.4)", Value(0));
CHECK_EVAL("DELTA(3;3)", Value(1));
// CHECK_EVAL("DELTA(1;TRUE)", Value(1));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDELTA(1.2; 3.4)", Value(0));
}
void TestEngineeringFunctions::testERF()
{
CHECK_EVAL("ERF(0.5)", Value(0.52049987781));
CHECK_EVAL("ERF(0.1)", Value(0.1124629160));
CHECK_EVAL("ABS(ERF(0.1; 0.5) - 0.40803696174) < 1e-6", Value(true));
CHECK_EVAL("ABS(ERF(0.1) - ERF(0.5) + 0.40803696174) < 1e-6", Value(true));
CHECK_EVAL("ABS(ERF(0.5; 0.1) + 0.40803696174) < 1e-6", Value(true));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERF(0.5)", Value(0.52049987781));
}
void TestEngineeringFunctions::testERFC()
{
CHECK_EVAL("ERFC(3)", Value(0.000022090497));
CHECK_EVAL("ABS(ERFC(0.1)-(1-ERF(0.1))) < 1e-6", Value(true));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERFC(3)", Value(0.000022090497));
}
void TestEngineeringFunctions::testGESTEP()
{
CHECK_EVAL("=GESTEP(1.2; 3.4)", Value(0));
CHECK_EVAL("=GESTEP(3; 3)", Value(1));
// CHECK_EVAL("=GESTEP(0.4; TRUE)", Value(0));
CHECK_EVAL("=GESTEP(4; 3)", Value(1));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGESTEP(1.2; 3.4)", Value(0));
}
void TestEngineeringFunctions::testHEX2BIN()
{
CHECK_EVAL("=HEX2BIN(\"a\")", Value("1010"));
CHECK_EVAL("=HEX2BIN(37)", Value("110111"));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2BIN(\"a\")", Value("1010"));
}
void TestEngineeringFunctions::testHEX2DEC()
{
CHECK_EVAL("=HEX2DEC(\"a\")", Value(10));
CHECK_EVAL("=HEX2DEC(37)", Value(55));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2DEC(\"a\")", Value(10));
}
void TestEngineeringFunctions::testHEX2OCT()
{
CHECK_EVAL("=HEX2OCT(\"a\")", Value("12"));
CHECK_EVAL("=HEX2OCT(37)", Value("67"));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2OCT(\"a\")", Value("12"));
}
void TestEngineeringFunctions::testIMABS()
{
CHECK_EVAL("=IMABS(COMPLEX(4;3))", 5.0);
CHECK_EVAL("=IMABS(COMPLEX(4;-3))", 5.0);
CHECK_EVAL("=IMABS(COMPLEX(-4;3))", 5.0);
CHECK_EVAL("=IMABS(COMPLEX(-4;-3))", 5.0);
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMABS(COMPLEX(4;3))", 5.0);
}
void TestEngineeringFunctions::testIMAGINARY()
{
CHECK_EVAL("=IMAGINARY(COMPLEX(4;-3))", -3.0);
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMAGINARY(COMPLEX(4;-3))", -3.0);
}
void TestEngineeringFunctions::testIMARGUMENT()
{
CHECK_EVAL("=IMARGUMENT(COMPLEX(3;4))", 0.927295218001612);
CHECK_EVAL("=IMARGUMENT(COMPLEX(3;-4))", -0.927295218001612);
CHECK_EVAL("=IMARGUMENT(COMPLEX(-3;4))", 2.214297435588180);
CHECK_EVAL("=IMARGUMENT(COMPLEX(-3;-4))", -2.214297435588180);
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMARGUMENT(COMPLEX(3;4))", 0.927295218001612);
}
void TestEngineeringFunctions::testIMCONJUGATE()
{
CHECK_EVAL("=IMABS(IMSUB(IMCONJUGATE(COMPLEX( 3; 4));COMPLEX( 3;-4)))", 0.0);
CHECK_EVAL("=IMABS(IMSUB(IMCONJUGATE(COMPLEX(-3;-4));COMPLEX(-3;4)))", 0.0);
CHECK_EVAL("=IMABS(IMSUB(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCONJUGATE(COMPLEX( 3; 4));COMPLEX( 3;-4)))", 0.0);
}
void TestEngineeringFunctions::testIMCOS()
{
CHECK_EVAL("=IMREAL ( IMCOS(COMPLEX(1;1)) )", 0.833730025131149);
CHECK_EVAL("=IMAGINARY( IMCOS(COMPLEX(1;1)) )", -0.988897705762865);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCOS(COMPLEX(1;1)))", 0.833730025131149);
}
void TestEngineeringFunctions::testIMCOSH()
{
CHECK_EVAL("=IMREAL ( IMCOSH(COMPLEX(1;1)) )", 0.833730025131149);
CHECK_EVAL("=IMAGINARY( IMCOSH(COMPLEX(1;1)) )", 0.988897705762865);
}
void TestEngineeringFunctions::testIMCOT()
{
CHECK_EVAL("=IMREAL ( IMCOT(COMPLEX(1;1)) )", 0.2176215618544027);
CHECK_EVAL("=IMAGINARY( IMCOT(COMPLEX(1;1)) )", -0.8680141428959249);
}
void TestEngineeringFunctions::testIMCSC()
{
CHECK_EVAL("=IMREAL ( IMCSC(COMPLEX(1;1)) )", 0.6215180171704284);
CHECK_EVAL("=IMAGINARY( IMCSC(COMPLEX(1;1)) )", -0.3039310016284264);
}
void TestEngineeringFunctions::testIMCSCH()
{
CHECK_EVAL("=IMREAL ( IMCSCH(COMPLEX(1;1)) )", 0.3039310016284264);
CHECK_EVAL("=IMAGINARY( IMCSCH(COMPLEX(1;1)) )", -0.6215180171704284);
}
void TestEngineeringFunctions::testIMDIV()
{
CHECK_EVAL("=IMREAL( IMDIV(COMPLEX(5;3); COMPLEX(1;-1)) )", 1.0);
CHECK_EVAL("=IMAGINARY( IMDIV(COMPLEX(5;3); COMPLEX(1;-1)) )", 4.0);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMDIV(COMPLEX(5;3);COMPLEX(1;-1)))", 1.0);
}
void TestEngineeringFunctions::testIMEXP()
{
CHECK_EVAL("=IMREAL( IMEXP(COMPLEX(1;2)) )", -1.13120438375681);
CHECK_EVAL("=IMAGINARY( IMEXP(COMPLEX(1;2)) )", 2.47172667200482);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMEXP(COMPLEX(1;2)))", -1.13120438375681);
}
void TestEngineeringFunctions::testIMLN()
{
CHECK_EVAL("=IMREAL( IMLN(COMPLEX(1;2)) )", 0.80471895621705);
CHECK_EVAL("=IMAGINARY( IMLN(COMPLEX(1;2)) )", 1.10714871779409);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLN(COMPLEX(1;2)))", 0.80471895621705);
}
void TestEngineeringFunctions::testIMLOG10()
{
CHECK_EVAL("=IMREAL(IMLOG10(COMPLEX(1;2)) )", 0.349485002168009);
CHECK_EVAL("=IMAGINARY( IMLOG10(COMPLEX(1;2)) )", 0.480828578784234);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG10(COMPLEX(1;2)) )", 0.349485002168009);
}
void TestEngineeringFunctions::testIMLOG2()
{
CHECK_EVAL("=IMREAL( IMLOG2(COMPLEX(1;2)) )", 1.16096404744368);
CHECK_EVAL("=IMAGINARY(IMLOG2(COMPLEX(1;2)) )", 1.59727796468811);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG2(COMPLEX(1;2)) )", 1.16096404744368);
}
void TestEngineeringFunctions::testIMPOWER()
{
CHECK_EVAL("=IMREAL( IMPOWER(COMPLEX(-1;2); 3) )", 11.0);
CHECK_EVAL("=IMAGINARY( IMPOWER(COMPLEX(-1;2); 3) )", -2.0);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPOWER(COMPLEX(-1;2);3))", 11.0);
}
void TestEngineeringFunctions::testIMPRODUCT()
{
CHECK_EVAL("=IMREAL( IMPRODUCT(COMPLEX(1;2); COMPLEX(-1;-2); COMPLEX(2;-3) ))", -6.0);
CHECK_EVAL("=IMAGINARY( IMPRODUCT(COMPLEX(1;2); COMPLEX(-1;-2); COMPLEX(2;-3) ))", -17.0);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPRODUCT(COMPLEX(1;2);COMPLEX(-1;-2);COMPLEX(2;-3)))", -6.0);
}
void TestEngineeringFunctions::testIMREAL()
{
CHECK_EVAL("=IMREAL(COMPLEX(4;-3))", 4.0);
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMREAL(COMPLEX(4;-3))", 4.0);
}
void TestEngineeringFunctions::testIMSEC()
{
CHECK_EVAL("=IMREAL ( IMSEC(COMPLEX(1;1)) )", 0.4983370305551868);
CHECK_EVAL("=IMAGINARY( IMSEC(COMPLEX(1;1)) )", 0.5910838417210450);
}
void TestEngineeringFunctions::testIMSECH()
{
CHECK_EVAL("=IMREAL ( IMSECH(COMPLEX(1;1)) )", 0.4983370305551868);
CHECK_EVAL("=IMAGINARY( IMSECH(COMPLEX(1;1)) )", -0.5910838417210450);
}
void TestEngineeringFunctions::testIMSIN()
{
CHECK_EVAL("=IMREAL ( IMSIN(COMPLEX(1;1)) )", 1.298457581415980);
CHECK_EVAL("=IMAGINARY( IMSIN(COMPLEX(1;1)) )", 0.634963914784736);
CHECK_EVAL("=IMREAL ( COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSIN(COMPLEX(1;1)) )", 1.298457581415980);
}
void TestEngineeringFunctions::testIMSINH()
{
CHECK_EVAL("=IMREAL ( IMSINH(COMPLEX(1;1)) )", 0.634963914784736);
CHECK_EVAL("=IMAGINARY( IMSINH(COMPLEX(1;1)) )", 1.298457581415980);
}
void TestEngineeringFunctions::testIMSQRT()
{
CHECK_EVAL("=IMREAL(IMSQRT(COMPLEX(1;-2)))", 1.272019649514070);
CHECK_EVAL("=IMAGINARY(IMSQRT(COMPLEX(1;-2)))", -0.786151377757423);
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSQRT(COMPLEX(1;-2)))", 1.272019649514070);
}
void TestEngineeringFunctions::testIMSUB()
{
CHECK_EVAL("=IMREAL( IMSUB(COMPLEX(5;3); COMPLEX(3;2)) )", 2.0);
CHECK_EVAL("=IMAGINARY( IMSUB(COMPLEX(5;3); COMPLEX(3;2)) )", 1.0);
CHECK_EVAL("=IMREAL(IMSUB(\"5-3i\";\"2i\"))", 5.0);
CHECK_EVAL("=IMAGINARY(IMSUB(\"5-3i\";\"2i\"))", -5.0);
CHECK_EVAL("=IMREAL( COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUB(COMPLEX(5;3); COMPLEX(3;2)) )", 2.0);
}
void TestEngineeringFunctions::testIMSUM()
{
CHECK_EVAL("=IMREAL(IMSUM(COMPLEX(1;2); COMPLEX(2;3) ))", 3.0);
CHECK_EVAL("=IMAGINARY(IMSUM( COMPLEX(1;2); COMPLEX(2;3) ))", 5.0);
// CHECK_EVAL( "=IMREAL( IMSUM(COMPLEX(1;2);B4:B5))", 6.0 );
// CHECK_EVAL( "=IMAGINARY( IMSUM(COMPLEX(1;2);B4:B5))", 9.0 );
CHECK_EVAL("=IMREAL(COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUM(COMPLEX(1;2); COMPLEX(2;3) ))", 3.0);
}
void TestEngineeringFunctions::testIMTAN()
{
CHECK_EVAL("=IMREAL ( IMTAN(COMPLEX(1;1)) )", 0.271752585319512);
CHECK_EVAL("=IMAGINARY( IMTAN(COMPLEX(1;1)) )", 1.083923327338690);
}
void TestEngineeringFunctions::testIMTANH()
{
CHECK_EVAL("=IMREAL ( IMTANH(COMPLEX(1;1)) )", 1.083923327338690);
CHECK_EVAL("=IMAGINARY( IMTANH(COMPLEX(1;1)) )", 0.271752585319512);
}
void TestEngineeringFunctions::testOCT2BIN()
{
CHECK_EVAL("=OCT2BIN(\"12\")", Value("1010"));
CHECK_EVAL("=OCT2BIN(\"55\")", Value("101101"));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2BIN(\"12\")", Value("1010"));
}
void TestEngineeringFunctions::testOCT2DEC()
{
CHECK_EVAL("=OCT2DEC(\"12\")", Value(10));
CHECK_EVAL("=OCT2DEC(\"55\")", Value(45));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2DEC(\"12\")", Value(10));
}
void TestEngineeringFunctions::testOCT2HEX()
{
CHECK_EVAL("=OCT2HEX(\"12\")", Value("A"));
CHECK_EVAL("=OCT2HEX(\"55\")", Value("2D"));
CHECK_EVAL("=COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2HEX(\"12\")", Value("A"));
}
-
-QTEST_KDEMAIN(TestEngineeringFunctions, GUI)
+
+QTEST_MAIN(TestEngineeringFunctions)
diff --git a/sheets/tests/TestEngineeringFunctions.h b/sheets/tests/TestEngineeringFunctions.h
index 362be1bd8e2..ba71f3ea3ce 100644
--- a/sheets/tests/TestEngineeringFunctions.h
+++ b/sheets/tests/TestEngineeringFunctions.h
@@ -1,93 +1,92 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_ENGINEERING_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_ENGINEERING_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestEngineeringFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testBIN2DEC();
void testBIN2OCT();
void testBIN2HEX();
void testCOMPLEX();
void testCONVERT();
void testDEC2HEX();
void testDEC2BIN();
void testDEC2OCT();
void testDELTA();
void testERF();
void testERFC();
void testGESTEP();
void testHEX2BIN();
void testHEX2DEC();
void testHEX2OCT();
void testIMABS();
void testIMAGINARY();
void testIMARGUMENT();
void testIMCONJUGATE();
void testIMCOS();
void testIMCOSH();
void testIMCOT();
void testIMCSC();
void testIMCSCH();
void testIMDIV();
void testIMEXP();
void testIMLN();
void testIMLOG10();
void testIMLOG2();
void testIMPOWER();
void testIMPRODUCT();
void testIMREAL();
void testIMSEC();
void testIMSECH();
void testIMSIN();
void testIMSINH();
void testIMSQRT();
void testIMSUB();
void testIMSUM();
void testIMTAN();
void testIMTANH();
void testOCT2BIN();
void testOCT2DEC();
void testOCT2HEX();
private:
Value evaluate(const QString&);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_ENGINEERING_FUNCTIONS
diff --git a/sheets/tests/TestFinancialFunctions.cpp b/sheets/tests/TestFinancialFunctions.cpp
index f01c3a664e4..91569941b86 100644
--- a/sheets/tests/TestFinancialFunctions.cpp
+++ b/sheets/tests/TestFinancialFunctions.cpp
@@ -1,1192 +1,1193 @@
/* This file is part of the KDE project
Copyright 2006 Ariya Hidayat <ariya@kde.org>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestFinancialFunctions.h"
#include "TestKspreadCommon.h"
+#include <QTest>
+
void TestFinancialFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
// - different algorithms among spreadsheet programs
// - precision limitation of floating-point number representation
// - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x,y) QCOMPARE(TestDouble(x,y,6),y)
#define CHECK_EVAL_SHORT(x,y) QCOMPARE(TestDouble(x,y,11),y)
#define CHECK_EVAL_EQUAL(x,y) QCOMPARE(TestSimple(x),y)
static Value TestDouble(const QString& formula, const Value& v2, int accuracy)
{
double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
bool res = fabs(v2.asFloat() - result.asFloat()) < epsilon;
if (!res)
kDebug(36002) << "check failed -->" << "Epsilon =" << epsilon << "" << (double)v2.asFloat() << " to" << (double)result.asFloat() << " diff =" << (double)(v2.asFloat() - result.asFloat());
// else
// kDebug(36002)<<"check -->" <<" diff =" << v2.asFloat()-result.asFloat();
if (res)
return v2;
else
return result;
}
static Value TestSimple(const QString& formula)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
return f.eval();
}
// ACCRINT
void TestFinancialFunctions::testACCRINT()
{
// odf test
CHECK_EVAL("ACCRINT( \"1992-12-01\"; \"1993-06-01\"; \"1993-07-01\"; 0.055; 100 ; 2; 0 ) ", Value(3.2083333333));
CHECK_EVAL("ACCRINT( \"2001-02-28\"; \"2001-08-31\";\"2001-05-01\"; 0.1 ; 1000; 2; 0 )",
Value(16.9444444444)); // A security is issued on 2.28.2001.
// First interest is set for 8.31.2001. The settlement date is 5.1.2001.
// The Rate is 0.1 or 10% and Par is 1000 currency units. Interest is paid
// half-yearly (frequency is 2). The basis is the US method (0). How much interest has accrued?
CHECK_EVAL("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 4; 0 )", Value(24.7222222222)); // leap year, quaterly, US (NASD) 30/360
CHECK_EVAL_SHORT("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 4; 1 )", Value(24.590164)); // leap year, quaterly, actual/acual
CHECK_EVAL("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 4; 2 )", Value(25)); // leap year, quaterly, actual/360
CHECK_EVAL_SHORT("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 4; 3 )", Value(24.657534)); // leap year, quaterly, actual/365
CHECK_EVAL("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 4; 4 )", Value(25)); // leap year, quaterly, European 30/360
CHECK_EVAL("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 1 )", Value(24.7222222222)); // leap year, annual, US (NASD) 30/360
CHECK_EVAL("ACCRINT( \"2004-02-01\"; \"2004-04-01\"; \"2004-05-01\"; 0.1; 1000; 2 )", Value(24.7222222222)); // leap year, semiannual, US 30/360
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINT( \"1992-12-01\";\"1993-06-01\";\"1993-07-01\";0.055;100;2;0)", Value(3.2083333333));
}
// ACCRINTM
void TestFinancialFunctions::testACCRINTM()
{
// kspread
CHECK_EVAL_SHORT("ACCRINTM( \"2001-04-01\"; \"2001-06-15\"; 0.1; 1000; 3 )", Value(20.5479454));
CHECK_EVAL_SHORT("ACCRINTM( \"2004-02-01\"; \"2004-05-01\"; 0.1; 1000; 0 )", Value(24.722222)); // leap year, US (NASD) 30/360
CHECK_EVAL_SHORT("ACCRINTM( \"2004-02-01\"; \"2004-05-01\"; 0.1; 1000; 1 )", Value(24.590164)); // leap year, actual/actual
CHECK_EVAL_SHORT("ACCRINTM( \"2004-02-01\"; \"2004-05-01\"; 0.1; 1000; 2 )", Value(25.0)); // leap year, actual/360
CHECK_EVAL_SHORT("ACCRINTM( \"2004-02-01\"; \"2004-05-01\"; 0.1; 1000; 3 )", Value(24.657534)); // leap year, actual/365
CHECK_EVAL_SHORT("ACCRINTM( \"2004-02-01\"; \"2004-05-01\"; 0.1; 1000; 4 )", Value(25.0)); // leap year, European 30/360
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINTM( \"2001-04-01\"; \"2001-06-15\"; 0.1; 1000; 3 )", Value(20.5479454));
}
// AMORDEGRC
void TestFinancialFunctions::testAMORDEGRC()
{
// Excel Formen und Funktionen
CHECK_EVAL("AMORDEGRC( 2400; 34199; 34334; 300; 1; 0.15; 1 )" , Value(775));
// bettersolution.com
CHECK_EVAL("AMORDEGRC( 50000; \"2003-01-01\"; \"2003-12-31\"; 500; 1; 0.15; 1 )" , Value(11738)); //
CHECK_EVAL("AMORDEGRC( 50000; \"2003-01-01\"; \"2003-12-31\"; 500; 2; 0.15; 1 )" , Value(7336)); //
CHECK_EVAL("AMORDEGRC( 50000; \"2003-01-01\"; \"2003-12-31\"; 500; 3; 0.15; 1 )" , Value(4585)); //
CHECK_EVAL("AMORDEGRC( 50000; \"2003-01-01\"; \"2003-12-31\"; 500; 4; 0.15; 1 )" , Value(2866)); //
// CHECK_EVAL_SHORT( "AMORDEGRC( 50000; \"2003-01-01\"; \"2003-12-31\"; 500; 5; 0.15; 1 )" , Value( 2388 ) ); // TODO check KSpread -> 1791
// odf tests
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 1 )" , Value(228)); // the first period (10 years life time)
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 8; 0.1; 1 )" , Value(26)); // (specs. 52) the period before last (10 years)
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 9; 0.1; 1 )" , Value(19)); // (specs. 52) the last period (10 years life time)
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 10; 0.1; 1 )" , Value(15)); // (specs. 15) - beyond life time (10 years life time)
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.25; 1 )" , Value(342)); // the first period (4 years life time)
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 0 )" , Value(229)); // leap year, US (NASD) 30/360
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 1 )" , Value(228)); // leap year, actual/actual
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 2 )" , Value(231)); // (specs 232) leap year, actual/360
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 3 )" , Value(228)); // leap year, actual/365
CHECK_EVAL("AMORDEGRC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 4 )" , Value(228)); // leap year, European 30/360
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORDEGRC(2400;34199;34334;300;1;0.15;1)", Value(775));
}
// AMORLINC
void TestFinancialFunctions::testAMORLINC()
{
CHECK_EVAL_SHORT("AMORLINC( 1000; \"2004-02-01\"; \"2004-12-31\"; 10; 0; 0.1; 1 )" , Value(91.2568306011)); // the first period (10 years life time)
CHECK_EVAL_SHORT("AMORLINC( 1000; \"2006-02-01\"; \"2006-12-31\"; 10; 0; 0.1; 3 )" , Value(91.2328767123)); // leap year, actual/365
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORLINC(1000;\"2004-02-01\";\"2004-12-31\";10;0;0.1;1)", Value(91.2568306011));
}
// COMPOUND
void TestFinancialFunctions::testCOMPOUND()
{
// kspread
CHECK_EVAL_SHORT("COMPOUND(5000;0.12;4;5)", Value(9030.556173));
}
// CONTINUOUS
void TestFinancialFunctions::testCONTINUOUS()
{
// kspread
CHECK_EVAL_SHORT("CONTINUOUS(1000;0.1;1)", Value(1105.17091808));
}
// COUPDAYBS
void TestFinancialFunctions::testCOUPDAYBS()
{
// ODF
CHECK_EVAL_SHORT("COUPDAYBS( DATE(1997;11;9); DATE(1999;11;15); 2 )", Value(174));
CHECK_EVAL_SHORT("COUPDAYBS( DATE(1997;11;9); DATE(1999;11;15); 2; 1 )", Value(178));
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 4; 0 )", Value(60)); // US (NASD) 30/360
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 4; 1 )", Value(60)); // actual/actual
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 4; 2 )", Value(60)); // actual/360
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 4; 3 )", Value(60)); // actual/365
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 4; 4 )", Value(60)); // European 30/360
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 1 )", Value(60)); // annual
CHECK_EVAL_SHORT("COUPDAYBS( DATE(2004;3;1); DATE(2009;1;1); 2 )", Value(60)); // semiannual
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYBS( DATE(1997;11;9); DATE(1999;11;15); 2 )", Value(174));
}
// COUPDAYS
void TestFinancialFunctions::testCOUPDAYS()
{
// ODF
CHECK_EVAL_SHORT("COUPDAYS( DATE(1997;11;9); DATE(1999;11;15); 2 )", Value(180));
CHECK_EVAL_SHORT("COUPDAYS( DATE(1997;11;9); DATE(1999;11;15); 2; 1 )", Value(184));
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 4; 0 )", Value(90)); // US (NASD) 30/360
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 4; 1 )", Value(91)); // actual/actual
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 4; 2 )", Value(90)); // actual/360
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 4; 3 )", Value(91.25)); // actual/365
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 4; 4 )", Value(90)); // European 30/360
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 1 )", Value(360)); // annual
CHECK_EVAL_SHORT("COUPDAYS( DATE(2004;2;1); DATE(2009;1;1); 2 )", Value(180)); // semiannual
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYS( DATE(1997;11;9); DATE(1999;11;15); 2 )", Value(180));
}
// COUPDAYSNC
void TestFinancialFunctions::testCOUPDAYSNC()
{
// ODF
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(1997;5;19); DATE(1999;11;15); 2 )", Value(176));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(1997;5;19); DATE(1999;11;15); 2; 1 )", Value(180));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 4; 0 )", Value(60));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 4; 1 )", Value(60));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 4; 2 )", Value(60));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 4; 3 )", Value(60));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 4; 4 )", Value(60));
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 1 )", Value(330)); // annual
CHECK_EVAL_SHORT("COUPDAYSNC( DATE(2004;2;1); DATE(2009;1;1); 2 )", Value(150)); // semiannual
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYSNC( DATE(1997;5;19); DATE(1999;11;15); 2 )", Value(176));
}
// COUPNCD
void TestFinancialFunctions::testCOUPNCD()
{
// ODF
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2007-01-01\"; 1; 1 )=DATE(2005;01;01)", Value(true)); // Annual
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2007-01-01\"; 2; 1 )=DATE(2004;07;01)", Value(true)); // Semiannual
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2007-01-01\"; 4; 1 )=DATE(2004;04;01)", Value(true)); // Quarterly
CHECK_EVAL_EQUAL("COUPNCD( \"2007-01-01\"; \"2007-01-01\"; 1; 1 )", Value::errorVALUE()); // settlement < maturity
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2009-01-01\"; 4; 0 )=DATE(2004;04;01)", Value(true));
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2009-01-01\"; 4; 1 )=DATE(2004;04;01)", Value(true));
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2009-01-01\"; 4; 2 )=DATE(2004;04;01)", Value(true));
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2009-01-01\"; 4; 3 )=DATE(2004;04;01)", Value(true));
CHECK_EVAL_EQUAL("COUPNCD( \"2004-01-01\"; \"2009-01-01\"; 4; 4 )=DATE(2004;04;01)", Value(true));
// alternate function name
CHECK_EVAL_EQUAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNCD( \"2004-01-01\"; \"2007-01-01\"; 1; 1 )=DATE(2005;01;01)", Value(true));
}
// COUPNUM
void TestFinancialFunctions::testCOUPNUM()
{
// ODF
CHECK_EVAL_SHORT("COUPNUM( \"2004-01-01\"; \"2007-01-01\"; 1; 1 )", Value(3)); // Annual
CHECK_EVAL_SHORT("COUPNUM( \"2004-01-01\"; \"2007-01-01\"; 2; 1 )", Value(6)); // Semiannual
CHECK_EVAL_SHORT("COUPNUM( \"2004-01-01\"; \"2007-01-01\"; 4; 1 )", Value(12)); // Quarterly
CHECK_EVAL_SHORT("COUPNUM( \"2004-02-01\"; \"2009-01-01\"; 4; 0 )", Value(20)); //
CHECK_EVAL_SHORT("COUPNUM( \"2004-02-01\"; \"2009-01-01\"; 4; 1 )", Value(20)); //
CHECK_EVAL_SHORT("COUPNUM( \"2004-02-01\"; \"2009-01-01\"; 4; 2 )", Value(20)); //
CHECK_EVAL_SHORT("COUPNUM( \"2004-02-01\"; \"2009-01-01\"; 4; 3 )", Value(20)); //
CHECK_EVAL_SHORT("COUPNUM( \"2004-02-01\"; \"2009-01-01\"; 4; 4 )", Value(20)); //
CHECK_EVAL_SHORT("COUPNUM( \"2004-01-01\"; \"2004-04-30\"; 12; 1 )", Value(4)); // Monthly, to end of month with less daysInMonth
CHECK_EVAL_SHORT("COUPNUM( \"2004-01-01\"; \"2004-05-01\"; 12; 1 )", Value(4)); // Monthly, to begin of month
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNUM(\"2004-01-01\";\"2007-01-01\";1;1)", Value(3));
}
// COUPPCD
void TestFinancialFunctions::testCOUPPCD()
{
// ODF
CHECK_EVAL_EQUAL("COUPPCD( \"2004-12-31\"; \"2007-01-01\"; 1; 1 )=DATE(2004;1;1)", Value(true)); // Annual
CHECK_EVAL_EQUAL("COUPPCD( \"2004-12-31\"; \"2007-01-01\"; 2; 1 )=DATE(2004;7;1)", Value(true)); // Semiannual
CHECK_EVAL_EQUAL("COUPPCD( \"2004-12-31\"; \"2007-01-01\"; 4; 1 )=DATE(2004;10;1)", Value(true)); // Quarterly
CHECK_EVAL_EQUAL("COUPPCD( \"2007-01-01\"; \"2004-01-01\"; 1; 1 )", Value::errorVALUE()); // settlement < maturity
CHECK_EVAL_EQUAL("COUPPCD( \"2004-02-29\"; \"2009-01-01\"; 4; 0 )=DATE(2004;01;01)", Value(true));
CHECK_EVAL_EQUAL("COUPPCD( \"2004-02-29\"; \"2009-01-01\"; 4; 1 )=DATE(2004;01;01)", Value(true));
CHECK_EVAL_EQUAL("COUPPCD( \"2004-02-29\"; \"2009-01-01\"; 4; 2 )=DATE(2004;01;01)", Value(true));
CHECK_EVAL_EQUAL("COUPPCD( \"2004-02-29\"; \"2009-01-01\"; 4; 3 )=DATE(2004;01;01)", Value(true));
CHECK_EVAL_EQUAL("COUPPCD( \"2004-02-29\"; \"2009-01-01\"; 4; 4 )=DATE(2004;01;01)", Value(true));
// alternate function name
CHECK_EVAL_EQUAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPPCD( \"2004-12-31\"; \"2007-01-01\"; 1; 1 )=DATE(2004;1;1)", Value(true));
}
// CUMIPMT
void TestFinancialFunctions::testCUMIPMT()
{
// ODF
CHECK_EVAL_SHORT("CUMIPMT( 0.06/12; 5*12; 100000; 5; 12; 0 )", Value(-3562.187023)); // maturity at the end of a period
CHECK_EVAL_SHORT("CUMIPMT( 0.06/12; 5*12; 100000; 5; 12; 1 )", Value(-3544.464699)); // maturity at the beginning of a period
CHECK_EVAL_SHORT("CUMIPMT( 0.06/12; 5*12; 100000; 0; 0; 0 )", Value(Value::errorVALUE())); // start > 0; end > 0
CHECK_EVAL_SHORT("CUMIPMT( 0.06/12; 5*12; 100000; 5; 61; 0 )", Value(Value::errorVALUE())); // end > periods
CHECK_EVAL_SHORT("CUMIPMT( 0.06/12; 5*12; 100000; 15; 12; 0 )", Value(Value::errorVALUE())); // start > end
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMIPMT(0.06/12;5*12;100000;5;12;0)", Value(-3562.187023));
}
// CUMPRINC
void TestFinancialFunctions::testCUMPRINC()
{
// ODF
CHECK_EVAL_SHORT("CUMPRINC( 0.06/12; 5*12; 100000; 5; 12; 0 )", Value(-11904.054201)); // maturity at the end of a period
CHECK_EVAL_SHORT("CUMPRINC( 0.06/12; 5*12; 100000; 5; 12; 1 )", Value(-11844.830051)); // maturity at the beginning of a period
CHECK_EVAL_SHORT("CUMPRINC( 0.06/12; 5*12; 100000; 0; 0; 0 )", Value(Value::errorVALUE())); // start > 0; end > 0
CHECK_EVAL_SHORT("CUMPRINC( 0.06/12; 5*12; 100000; 5; 61; 0 )", Value(Value::errorVALUE())); // end > periods
CHECK_EVAL_SHORT("CUMPRINC( 0.06/12; 5*12; 100000;15; 12; 0 )", Value(Value::errorVALUE())); // start > end
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMPRINC(0.06/12;5*12;100000;5;12;0)", Value(-11904.054201));
}
// Fixed-declining balance depreciation
// DB(cost, salvage, life, period, month)
void TestFinancialFunctions::testDB()
{
// Excel example: http://office.microsoft.com/en-us/excel/HP100623551033.aspx
CHECK_EVAL("DB(1000000; 100000; 6; 1; 7)", Value(186083.3333333333));
CHECK_EVAL("DB(1000000; 100000; 6; 2; 7)", Value(259639.4166666667));
CHECK_EVAL("DB(1000000; 100000; 6; 3; 7)", Value(176814.4427500000));
CHECK_EVAL("DB(1000000; 100000; 6; 4; 7)", Value(120410.6355127500));
CHECK_EVAL("DB(1000000; 100000; 6; 5; 7)", Value(81999.64278418274));
CHECK_EVAL("DB(1000000; 100000; 6; 6; 7)", Value(55841.75673602846));
CHECK_EVAL("DB(1000000; 100000; 6; 7; 7)", Value(15845.09847384807));
// http://www.vni.com/products/imsl/jmsl/v30/api/com/imsl/finance/dbEx1.html
CHECK_EVAL("DB(2500; 500; 3; 1; 6)", Value(518.750000000000));
CHECK_EVAL("DB(2500; 500; 3; 2; 6)", Value(822.218750000000));
CHECK_EVAL("DB(2500; 500; 3; 3; 6)", Value(480.997968750000));
CHECK_EVAL("DB(2500; 500; 3; 4; 6)", Value(140.691905859375));
// test cases in OpenFormula specification
CHECK_EVAL("DB(4000;500;4;2)", Value(963.90));
CHECK_EVAL("DB(4000;500;4;2;2)", Value(1510.65));
CHECK_EVAL("DB(4000;500;4;5)", Value(0.0));
CHECK_EVAL("DB(0;500;4;2)", Value(Value::errorNUM()));
CHECK_EVAL("DB(4000;-500;4;2)", Value(Value::errorNUM()));
CHECK_EVAL("DB(4000;500;0;0)", Value(Value::errorNUM()));
CHECK_EVAL("DB(4000;500;2;0)", Value(Value::errorNUM()));
}
// Double declining balance depreciation
// DDB(cost, salvage, life, period, factor)
void TestFinancialFunctions::testDDB()
{
// Excel example: http://office.microsoft.com/en-us/excel/HP100623561033.aspx
CHECK_EVAL("DDB(2400; 300; 10*365; 1; 2)", Value(1.31506849315065));
CHECK_EVAL("DDB(2400; 300; 10*12; 1; 2)", Value(40.0));
CHECK_EVAL("DDB(2400; 300; 10; 1; 2)", Value(480.0));
CHECK_EVAL("DDB(2400; 300; 10; 2; 1.5)", Value(306));
CHECK_EVAL("DDB(2400; 300; 10; 10; 2)", Value(22.1225472000002));
// http://www.vni.com/products/imsl/jmsl/v30/api/com/imsl/finance/ddbEx1.html
CHECK_EVAL("DDB(2500; 500; 24; 1; 2)", Value(208.333333333333));
CHECK_EVAL("DDB(2500; 500; 24; 2; 2)", Value(190.972222222222));
CHECK_EVAL("DDB(2500; 500; 24; 3; 2)", Value(175.057870370370));
CHECK_EVAL("DDB(2500; 500; 24; 4; 2)", Value(160.469714506173));
CHECK_EVAL("DDB(2500; 500; 24; 5; 2)", Value(147.097238297325));
CHECK_EVAL("DDB(2500; 500; 24; 6; 2)", Value(134.839135105881));
CHECK_EVAL("DDB(2500; 500; 24; 7; 2)", Value(123.602540513725));
CHECK_EVAL("DDB(2500; 500; 24; 8; 2)", Value(113.302328804248));
CHECK_EVAL("DDB(2500; 500; 24; 9; 2)", Value(103.860468070560));
CHECK_EVAL("DDB(2500; 500; 24; 10; 2)", Value(95.2054290646802));
CHECK_EVAL("DDB(2500; 500; 24; 11; 2)", Value(87.2716433092901));
CHECK_EVAL("DDB(2500; 500; 24; 12; 2)", Value(79.9990063668494));
CHECK_EVAL("DDB(2500; 500; 24; 13; 2)", Value(73.3324225029452));
CHECK_EVAL("DDB(2500; 500; 24; 14; 2)", Value(67.2213872943665));
CHECK_EVAL("DDB(2500; 500; 24; 15; 2)", Value(61.6196050198359));
CHECK_EVAL("DDB(2500; 500; 24; 16; 2)", Value(56.4846379348497));
CHECK_EVAL("DDB(2500; 500; 24; 17; 2)", Value(51.7775847736120));
CHECK_EVAL("DDB(2500; 500; 24; 18; 2)", Value(47.4627860424778));
CHECK_EVAL("DDB(2500; 500; 24; 19; 2)", Value(22.0906464672553));
CHECK_EVAL("DDB(2500; 500; 24; 20; 2)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 21; 2)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 22; 2)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 23; 2)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 24; 2)", Value(0));
// test cases in OpenFormula specification
CHECK_EVAL("DDB(4000; 500; 4; 2; 2)", Value(1000));
CHECK_EVAL("DDB(4000; 500; 4; 2)", Value(1000));
CHECK_EVAL("DDB(1100; 100; 5; 5; 2.3 )", Value(0));
// try default factor (=2)
CHECK_EVAL("DDB(2400; 300; 10*12; 1)", Value(40.0));
CHECK_EVAL("DDB(2400; 300; 10; 1)", Value(480.0));
CHECK_EVAL("DDB(2500; 500; 24; 22)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 23)", Value(0));
CHECK_EVAL("DDB(2500; 500; 24; 24)", Value(0));
// factor > life
CHECK_EVAL("DDB(2400; 300; 10; 0.8; 20)", Value(2100));
CHECK_EVAL("DDB(2400; 300; 10; 1.0; 20)", Value(2100));
CHECK_EVAL("DDB(2400; 300; 10; 1.2; 20)", Value(0));
// factor is fraction
CHECK_EVAL("DDB(2400; 300; 10; 2; 2.5)", Value(450));
CHECK_EVAL("DDB(2400; 300; 10; 2; 1.5)", Value(306));
// period is fraction
CHECK_EVAL("DDB(2400; 300; 10; 6.7; 2)", Value(134.5408487904432));
CHECK_EVAL("DDB(2400; 300; 10; 7.7; 2)", Value(107.6326790323546));
}
// DISC
void TestFinancialFunctions::testDISC()
{
// basis | day-count basis
//-------+-----------------------------------
// 0 | US (NASD) 30/360
// 1 | Actual/actual (Euro), also known as AFB
// 2 | Actual/360
// 3 | Actual/365
// 4 | European 30/360
CHECK_EVAL_SHORT("DISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 0)", Value(0.010339));
CHECK_EVAL_SHORT("DISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 1)", Value(0.010333)); // NOK (0.010332)
CHECK_EVAL_SHORT("DISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 2)", Value(0.010181));
CHECK_EVAL_SHORT("DISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 3)", Value(0.010322));
CHECK_EVAL_SHORT("DISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 4)", Value(0.010333));
CHECK_EVAL_SHORT("DISC( DATE(2006;01;01); date(2008;01;01); 200; 100; 3)", Value(-0.500000));
CHECK_EVAL_SHORT("DISC( DATE(2006;01;01); date(2005;07;01); 95000; 100000; 4)", Value(false));
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDISC( DATE(2004;02;29); date(2009;01;01); 95000; 100000; 0)", Value(0.010339));
}
// DOLLARDE
void TestFinancialFunctions::testDOLLARDE()
{
// http://publib.boulder.ibm.com/infocenter/iadthelp/v7r0/index.jsp?topic=/com.businessobjects.integration.eclipse.designer.doc/designer/Functions68.html
CHECK_EVAL_SHORT("DOLLARDE( 1.1 ; 8)" , Value(1.125)); //
CHECK_EVAL_SHORT("DOLLARDE( 2.13;16)" , Value(2.8125)); //
CHECK_EVAL_SHORT("DOLLARDE( 2.45;16)" , Value(4.8125)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.16; 8)" , Value(1.2)); //
// http://www.bettersolutions.com/excel/EDH113/LR849116511.htm
CHECK_EVAL_SHORT("DOLLARDE( 1.1 ; 2)" , Value(1.5)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.25; 5)" , Value(1.5)); //
CHECK_EVAL_SHORT("DOLLARDE( 5.08; 4)" , Value(5.2)); //
CHECK_EVAL_SHORT("DOLLARDE( 5.24; 4)" , Value(5.6)); //
CHECK_EVAL_SHORT("DOLLARDE( 100.24; 4)" , Value(100.6)); //
CHECK_EVAL_SHORT("DOLLARFR(DOLLARDE( 101.2; 4);4)", Value(101.2)); // for- and backward
// ODF
CHECK_EVAL_SHORT("DOLLARDE( 1.1; 4)" , Value(1.25)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.1; 3)" , Value(1.333333)); //
CHECK_EVAL_SHORT("DOLLARDE( -1.1;10)" , Value(-1.1)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.0; 5)" , Value(1)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.1;10)" , Value(1.1)); //
CHECK_EVAL_SHORT("DOLLARDE( 1.1; 0)" , Value::errorVALUE()); //
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARDE(1.1;8)" , Value(1.125));
}
// DOLLARFR
void TestFinancialFunctions::testDOLLARFR()
{
// my tests
CHECK_EVAL_SHORT("DOLLARFR( 1.1 ; 9)" , Value(1.09)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.1 ; 11)" , Value(1.011)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.1 ; 10)" , Value(1.1)); //
// http://www.bettersolutions.com/excel/EDH113/QR810212321.htm
CHECK_EVAL_SHORT("DOLLARFR( 1.125 ; 8)" , Value(1.1)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.5 ; 2)" , Value(1.1)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.5 ; 8)" , Value(1.4)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.5 ; 5)" , Value(1.25)); //
// ODF
CHECK_EVAL_SHORT("DOLLARFR( 1.1 ;10)" , Value(1.1)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.25; 4)" , Value(1.1)); //
CHECK_EVAL_SHORT("DOLLARFR(-1.33333; 3)" , Value(-1.099999)); // ODF specs error (1.1) must be -1.1
CHECK_EVAL_SHORT("DOLLARFR( 1.0; 5)" , Value(1)); //
CHECK_EVAL_SHORT("DOLLARFR( 1.1; 0)" , Value::errorVALUE()); //
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARFR( 1.1 ; 9)" , Value(1.09));
}
// DURATION
void TestFinancialFunctions::testDURATION()
{
// kspread
CHECK_EVAL("DURATION( 0.1; 1000; 2000 )" , Value(7.2725408973)); //
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDURATION( 0.1; 1000; 2000 )" , Value(7.2725408973));
}
// DURATION_ADD
void TestFinancialFunctions::testDURATION_ADD()
{
CHECK_EVAL("DURATION_ADD( \"1998-01-01\"; \"2006-01-01\"; 0.08; 0.09; 2; 1 )" , Value(5.9937749555)); //
}
// EFFECT
void TestFinancialFunctions::testEFFECT()
{
// kspread
CHECK_EVAL_SHORT("EFFECT(0.08;12)", Value(0.083));
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEFFECT(0.08;12)", Value(0.083));
}
// Euro conversion
// EURO(currency)
void TestFinancialFunctions::testEURO()
{
CHECK_EVAL("EURO(\"ATS\")", Value(13.7603));
CHECK_EVAL("EURO(\"BEF\")", Value(40.3399));
CHECK_EVAL("EURO(\"DEM\")", Value(1.95583));
CHECK_EVAL("EURO(\"ESP\")", Value(166.386));
CHECK_EVAL("EURO(\"EUR\")", Value(1.0));
CHECK_EVAL("EURO(\"FIM\")", Value(5.94573));
CHECK_EVAL("EURO(\"FRF\")", Value(6.55957));
CHECK_EVAL("EURO(\"GRD\")", Value(340.75));
CHECK_EVAL("EURO(\"IEP\")", Value(0.787564));
CHECK_EVAL("EURO(\"ITL\")", Value(1936.27));
CHECK_EVAL("EURO(\"LUX\")", Value(40.3399));
CHECK_EVAL("EURO(\"NLG\")", Value(2.20371));
CHECK_EVAL("EURO(\"PTE\")", Value(200.482));
// should still work with lowercase
CHECK_EVAL("EURO(\"ats\")", Value(13.7603));
CHECK_EVAL("EURO(\"bef\")", Value(40.3399));
CHECK_EVAL("EURO(\"dem\")", Value(1.95583));
CHECK_EVAL("EURO(\"esp\")", Value(166.386));
CHECK_EVAL("EURO(\"eur\")", Value(1.0));
CHECK_EVAL("EURO(\"fim\")", Value(5.94573));
CHECK_EVAL("EURO(\"frf\")", Value(6.55957));
CHECK_EVAL("EURO(\"grd\")", Value(340.75));
CHECK_EVAL("EURO(\"iep\")", Value(0.787564));
CHECK_EVAL("EURO(\"itl\")", Value(1936.27));
CHECK_EVAL("EURO(\"lux\")", Value(40.3399));
CHECK_EVAL("EURO(\"nlg\")", Value(2.20371));
CHECK_EVAL("EURO(\"pte\")", Value(200.482));
// should still work with mixed-case
CHECK_EVAL("EURO(\"Ats\")", Value(13.7603));
CHECK_EVAL("EURO(\"Bef\")", Value(40.3399));
CHECK_EVAL("EURO(\"Dem\")", Value(1.95583));
CHECK_EVAL("EURO(\"Esp\")", Value(166.386));
CHECK_EVAL("EURO(\"Eur\")", Value(1.0));
CHECK_EVAL("EURO(\"Fim\")", Value(5.94573));
CHECK_EVAL("EURO(\"Frf\")", Value(6.55957));
CHECK_EVAL("EURO(\"GrD\")", Value(340.75));
CHECK_EVAL("EURO(\"IeP\")", Value(0.787564));
CHECK_EVAL("EURO(\"Itl\")", Value(1936.27));
CHECK_EVAL("EURO(\"luX\")", Value(40.3399));
CHECK_EVAL("EURO(\"nlG\")", Value(2.20371));
CHECK_EVAL("EURO(\"ptE\")", Value(200.482));
CHECK_EVAL("EURO(\"NOMANSLAND\")", Value::errorNUM());
}
// Currency conversion using Euro
// EUROCONVERT(number,source,target)
void TestFinancialFunctions::testEUROCONVERT()
{
// 1 Euro to ...
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"ATS\")", Value(13.7603));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"BEF\")", Value(40.3399));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"DEM\")", Value(1.95583));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"ESP\")", Value(166.386));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"EUR\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"FIM\")", Value(5.94573));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"FRF\")", Value(6.55957));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"GRD\")", Value(340.75));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"IEP\")", Value(0.787564));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"ITL\")", Value(1936.27));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"LUX\")", Value(40.3399));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"NLG\")", Value(2.20371));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"PTE\")", Value(200.482));
// identity
CHECK_EVAL("EUROCONVERT(1;\"BEF\";\"bef\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"DEM\";\"dem\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"ESP\";\"esp\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"EUR\";\"eur\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"FIM\";\"fim\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"FRF\";\"frf\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"GRD\";\"grd\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"IEP\";\"iep\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"ITL\";\"itl\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"LUX\";\"lux\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"NLG\";\"nlg\")", Value(1.0));
CHECK_EVAL("EUROCONVERT(1;\"PTE\";\"pte\")", Value(1.0));
// all other combinations
CHECK_EVAL("EUROCONVERT( 2; \"ATS\"; \"bef\" )", Value(2*40.3399 / 13.7603));
CHECK_EVAL("EUROCONVERT( 3; \"ATS\"; \"dem\" )", Value(3*1.95583 / 13.7603));
CHECK_EVAL("EUROCONVERT( 4; \"ATS\"; \"esp\" )", Value(4*166.386 / 13.7603));
CHECK_EVAL("EUROCONVERT( 5; \"ATS\"; \"eur\" )", Value(5*1 / 13.7603));
CHECK_EVAL("EUROCONVERT( 6; \"ATS\"; \"fim\" )", Value(6*5.94573 / 13.7603));
CHECK_EVAL("EUROCONVERT( 7; \"ATS\"; \"frf\" )", Value(7*6.55957 / 13.7603));
CHECK_EVAL("EUROCONVERT( 8; \"ATS\"; \"grd\" )", Value(8*340.75 / 13.7603));
CHECK_EVAL("EUROCONVERT( 9; \"ATS\"; \"iep\" )", Value(9*0.787564 / 13.7603));
CHECK_EVAL("EUROCONVERT( 10; \"ATS\"; \"itl\" )", Value(10*1936.27 / 13.7603));
CHECK_EVAL("EUROCONVERT( 11; \"ATS\"; \"lux\" )", Value(11*40.3399 / 13.7603));
CHECK_EVAL("EUROCONVERT( 12; \"ATS\"; \"nlg\" )", Value(12*2.20371 / 13.7603));
CHECK_EVAL("EUROCONVERT( 13; \"ATS\"; \"pte\" )", Value(13*200.482 / 13.7603));
CHECK_EVAL("EUROCONVERT( 14; \"BEF\"; \"ats\" )", Value(14*13.7603 / 40.3399));
CHECK_EVAL("EUROCONVERT( 15; \"BEF\"; \"dem\" )", Value(15*1.95583 / 40.3399));
CHECK_EVAL("EUROCONVERT( 16; \"BEF\"; \"esp\" )", Value(16*166.386 / 40.3399));
CHECK_EVAL("EUROCONVERT( 17; \"BEF\"; \"eur\" )", Value(17*1 / 40.3399));
CHECK_EVAL("EUROCONVERT( 18; \"BEF\"; \"fim\" )", Value(18*5.94573 / 40.3399));
CHECK_EVAL("EUROCONVERT( 19; \"BEF\"; \"frf\" )", Value(19*6.55957 / 40.3399));
CHECK_EVAL("EUROCONVERT( 20; \"BEF\"; \"grd\" )", Value(20*340.75 / 40.3399));
CHECK_EVAL("EUROCONVERT( 21; \"BEF\"; \"iep\" )", Value(21*0.787564 / 40.3399));
CHECK_EVAL("EUROCONVERT( 22; \"BEF\"; \"itl\" )", Value(22*1936.27 / 40.3399));
CHECK_EVAL("EUROCONVERT( 23; \"BEF\"; \"lux\" )", Value(23*40.3399 / 40.3399));
CHECK_EVAL("EUROCONVERT( 24; \"BEF\"; \"nlg\" )", Value(24*2.20371 / 40.3399));
CHECK_EVAL("EUROCONVERT( 25; \"BEF\"; \"pte\" )", Value(25*200.482 / 40.3399));
CHECK_EVAL("EUROCONVERT( 26; \"DEM\"; \"ats\" )", Value(26*13.7603 / 1.95583));
CHECK_EVAL("EUROCONVERT( 27; \"DEM\"; \"bef\" )", Value(27*40.3399 / 1.95583));
CHECK_EVAL("EUROCONVERT( 28; \"DEM\"; \"esp\" )", Value(28*166.386 / 1.95583));
CHECK_EVAL("EUROCONVERT( 29; \"DEM\"; \"eur\" )", Value(29*1 / 1.95583));
CHECK_EVAL("EUROCONVERT( 30; \"DEM\"; \"fim\" )", Value(30*5.94573 / 1.95583));
CHECK_EVAL("EUROCONVERT( 31; \"DEM\"; \"frf\" )", Value(31*6.55957 / 1.95583));
CHECK_EVAL("EUROCONVERT( 32; \"DEM\"; \"grd\" )", Value(32*340.75 / 1.95583));
CHECK_EVAL("EUROCONVERT( 33; \"DEM\"; \"iep\" )", Value(33*0.787564 / 1.95583));
CHECK_EVAL("EUROCONVERT( 34; \"DEM\"; \"itl\" )", Value(34*1936.27 / 1.95583));
CHECK_EVAL("EUROCONVERT( 35; \"DEM\"; \"lux\" )", Value(35*40.3399 / 1.95583));
CHECK_EVAL("EUROCONVERT( 36; \"DEM\"; \"nlg\" )", Value(36*2.20371 / 1.95583));
CHECK_EVAL("EUROCONVERT( 37; \"DEM\"; \"pte\" )", Value(37*200.482 / 1.95583));
CHECK_EVAL("EUROCONVERT( 38; \"ESP\"; \"ats\" )", Value(38*13.7603 / 166.386));
CHECK_EVAL("EUROCONVERT( 39; \"ESP\"; \"bef\" )", Value(39*40.3399 / 166.386));
CHECK_EVAL("EUROCONVERT( 40; \"ESP\"; \"dem\" )", Value(40*1.95583 / 166.386));
CHECK_EVAL("EUROCONVERT( 41; \"ESP\"; \"eur\" )", Value(41*1 / 166.386));
CHECK_EVAL("EUROCONVERT( 42; \"ESP\"; \"fim\" )", Value(42*5.94573 / 166.386));
CHECK_EVAL("EUROCONVERT( 43; \"ESP\"; \"frf\" )", Value(43*6.55957 / 166.386));
CHECK_EVAL("EUROCONVERT( 44; \"ESP\"; \"grd\" )", Value(44*340.75 / 166.386));
CHECK_EVAL("EUROCONVERT( 45; \"ESP\"; \"iep\" )", Value(45*0.787564 / 166.386));
CHECK_EVAL("EUROCONVERT( 46; \"ESP\"; \"itl\" )", Value(46*1936.27 / 166.386));
CHECK_EVAL("EUROCONVERT( 47; \"ESP\"; \"lux\" )", Value(47*40.3399 / 166.386));
CHECK_EVAL("EUROCONVERT( 48; \"ESP\"; \"nlg\" )", Value(48*2.20371 / 166.386));
CHECK_EVAL("EUROCONVERT( 49; \"ESP\"; \"pte\" )", Value(49*200.482 / 166.386));
CHECK_EVAL("EUROCONVERT( 50; \"EUR\"; \"ats\" )", Value(50*13.7603 / 1));
CHECK_EVAL("EUROCONVERT( 51; \"EUR\"; \"bef\" )", Value(51*40.3399 / 1));
CHECK_EVAL("EUROCONVERT( 52; \"EUR\"; \"dem\" )", Value(52*1.95583 / 1));
CHECK_EVAL("EUROCONVERT( 53; \"EUR\"; \"esp\" )", Value(53*166.386 / 1));
CHECK_EVAL("EUROCONVERT( 54; \"EUR\"; \"fim\" )", Value(54*5.94573 / 1));
CHECK_EVAL("EUROCONVERT( 55; \"EUR\"; \"frf\" )", Value(55*6.55957 / 1));
CHECK_EVAL("EUROCONVERT( 56; \"EUR\"; \"grd\" )", Value(56*340.75 / 1));
CHECK_EVAL("EUROCONVERT( 57; \"EUR\"; \"iep\" )", Value(57*0.787564 / 1));
CHECK_EVAL("EUROCONVERT( 58; \"EUR\"; \"itl\" )", Value(58*1936.27 / 1));
CHECK_EVAL("EUROCONVERT( 59; \"EUR\"; \"lux\" )", Value(59*40.3399 / 1));
CHECK_EVAL("EUROCONVERT( 60; \"EUR\"; \"nlg\" )", Value(60*2.20371 / 1));
CHECK_EVAL("EUROCONVERT( 61; \"EUR\"; \"pte\" )", Value(61*200.482 / 1));
CHECK_EVAL("EUROCONVERT( 62; \"FIM\"; \"ats\" )", Value(62*13.7603 / 5.94573));
CHECK_EVAL("EUROCONVERT( 63; \"FIM\"; \"bef\" )", Value(63*40.3399 / 5.94573));
CHECK_EVAL("EUROCONVERT( 64; \"FIM\"; \"dem\" )", Value(64*1.95583 / 5.94573));
CHECK_EVAL("EUROCONVERT( 65; \"FIM\"; \"esp\" )", Value(65*166.386 / 5.94573));
CHECK_EVAL("EUROCONVERT( 66; \"FIM\"; \"eur\" )", Value(66*1 / 5.94573));
CHECK_EVAL("EUROCONVERT( 67; \"FIM\"; \"frf\" )", Value(67*6.55957 / 5.94573));
CHECK_EVAL("EUROCONVERT( 68; \"FIM\"; \"grd\" )", Value(68*340.75 / 5.94573));
CHECK_EVAL("EUROCONVERT( 69; \"FIM\"; \"iep\" )", Value(69*0.787564 / 5.94573));
CHECK_EVAL("EUROCONVERT( 70; \"FIM\"; \"itl\" )", Value(70*1936.27 / 5.94573));
CHECK_EVAL("EUROCONVERT( 71; \"FIM\"; \"lux\" )", Value(71*40.3399 / 5.94573));
CHECK_EVAL("EUROCONVERT( 72; \"FIM\"; \"nlg\" )", Value(72*2.20371 / 5.94573));
CHECK_EVAL("EUROCONVERT( 73; \"FIM\"; \"pte\" )", Value(73*200.482 / 5.94573));
CHECK_EVAL("EUROCONVERT( 74; \"FRF\"; \"ats\" )", Value(74*13.7603 / 6.55957));
CHECK_EVAL("EUROCONVERT( 75; \"FRF\"; \"bef\" )", Value(75*40.3399 / 6.55957));
CHECK_EVAL("EUROCONVERT( 76; \"FRF\"; \"dem\" )", Value(76*1.95583 / 6.55957));
CHECK_EVAL("EUROCONVERT( 77; \"FRF\"; \"esp\" )", Value(77*166.386 / 6.55957));
CHECK_EVAL("EUROCONVERT( 78; \"FRF\"; \"eur\" )", Value(78*1 / 6.55957));
CHECK_EVAL("EUROCONVERT( 79; \"FRF\"; \"fim\" )", Value(79*5.94573 / 6.55957));
CHECK_EVAL("EUROCONVERT( 80; \"FRF\"; \"grd\" )", Value(80*340.75 / 6.55957));
CHECK_EVAL("EUROCONVERT( 81; \"FRF\"; \"iep\" )", Value(81*0.787564 / 6.55957));
CHECK_EVAL("EUROCONVERT( 82; \"FRF\"; \"itl\" )", Value(82*1936.27 / 6.55957));
CHECK_EVAL("EUROCONVERT( 83; \"FRF\"; \"lux\" )", Value(83*40.3399 / 6.55957));
CHECK_EVAL("EUROCONVERT( 84; \"FRF\"; \"nlg\" )", Value(84*2.20371 / 6.55957));
CHECK_EVAL("EUROCONVERT( 85; \"FRF\"; \"pte\" )", Value(85*200.482 / 6.55957));
CHECK_EVAL("EUROCONVERT( 86; \"GRD\"; \"ats\" )", Value(86*13.7603 / 340.75));
CHECK_EVAL("EUROCONVERT( 87; \"GRD\"; \"bef\" )", Value(87*40.3399 / 340.75));
CHECK_EVAL("EUROCONVERT( 88; \"GRD\"; \"dem\" )", Value(88*1.95583 / 340.75));
CHECK_EVAL("EUROCONVERT( 89; \"GRD\"; \"esp\" )", Value(89*166.386 / 340.75));
CHECK_EVAL("EUROCONVERT( 90; \"GRD\"; \"eur\" )", Value(90*1 / 340.75));
CHECK_EVAL("EUROCONVERT( 91; \"GRD\"; \"fim\" )", Value(91*5.94573 / 340.75));
CHECK_EVAL("EUROCONVERT( 92; \"GRD\"; \"frf\" )", Value(92*6.55957 / 340.75));
CHECK_EVAL("EUROCONVERT( 93; \"GRD\"; \"iep\" )", Value(93*0.787564 / 340.75));
CHECK_EVAL("EUROCONVERT( 94; \"GRD\"; \"itl\" )", Value(94*1936.27 / 340.75));
CHECK_EVAL("EUROCONVERT( 95; \"GRD\"; \"lux\" )", Value(95*40.3399 / 340.75));
CHECK_EVAL("EUROCONVERT( 96; \"GRD\"; \"nlg\" )", Value(96*2.20371 / 340.75));
CHECK_EVAL("EUROCONVERT( 97; \"GRD\"; \"pte\" )", Value(97*200.482 / 340.75));
CHECK_EVAL("EUROCONVERT( 98; \"IEP\"; \"ats\" )", Value(98*13.7603 / 0.787564));
CHECK_EVAL("EUROCONVERT( 99; \"IEP\"; \"bef\" )", Value(99*40.3399 / 0.787564));
CHECK_EVAL("EUROCONVERT( 100; \"IEP\"; \"dem\" )", Value(100*1.95583 / 0.787564));
CHECK_EVAL("EUROCONVERT( 101; \"IEP\"; \"esp\" )", Value(101*166.386 / 0.787564));
CHECK_EVAL("EUROCONVERT( 102; \"IEP\"; \"eur\" )", Value(102*1 / 0.787564));
CHECK_EVAL("EUROCONVERT( 103; \"IEP\"; \"fim\" )", Value(103*5.94573 / 0.787564));
CHECK_EVAL("EUROCONVERT( 104; \"IEP\"; \"frf\" )", Value(104*6.55957 / 0.787564));
CHECK_EVAL("EUROCONVERT( 105; \"IEP\"; \"grd\" )", Value(105*340.75 / 0.787564));
CHECK_EVAL("EUROCONVERT( 106; \"IEP\"; \"itl\" )", Value(106*1936.27 / 0.787564));
CHECK_EVAL("EUROCONVERT( 107; \"IEP\"; \"lux\" )", Value(107*40.3399 / 0.787564));
CHECK_EVAL("EUROCONVERT( 108; \"IEP\"; \"nlg\" )", Value(108*2.20371 / 0.787564));
CHECK_EVAL("EUROCONVERT( 109; \"IEP\"; \"pte\" )", Value(109*200.482 / 0.787564));
CHECK_EVAL("EUROCONVERT( 110; \"ITL\"; \"ats\" )", Value(110*13.7603 / 1936.27));
CHECK_EVAL("EUROCONVERT( 111; \"ITL\"; \"bef\" )", Value(111*40.3399 / 1936.27));
CHECK_EVAL("EUROCONVERT( 112; \"ITL\"; \"dem\" )", Value(112*1.95583 / 1936.27));
CHECK_EVAL("EUROCONVERT( 113; \"ITL\"; \"esp\" )", Value(113*166.386 / 1936.27));
CHECK_EVAL("EUROCONVERT( 114; \"ITL\"; \"eur\" )", Value(114*1 / 1936.27));
CHECK_EVAL("EUROCONVERT( 115; \"ITL\"; \"fim\" )", Value(115*5.94573 / 1936.27));
CHECK_EVAL("EUROCONVERT( 116; \"ITL\"; \"frf\" )", Value(116*6.55957 / 1936.27));
CHECK_EVAL("EUROCONVERT( 117; \"ITL\"; \"grd\" )", Value(117*340.75 / 1936.27));
CHECK_EVAL("EUROCONVERT( 118; \"ITL\"; \"iep\" )", Value(118*0.787564 / 1936.27));
CHECK_EVAL("EUROCONVERT( 119; \"ITL\"; \"lux\" )", Value(119*40.3399 / 1936.27));
CHECK_EVAL("EUROCONVERT( 120; \"ITL\"; \"nlg\" )", Value(120*2.20371 / 1936.27));
CHECK_EVAL("EUROCONVERT( 121; \"ITL\"; \"pte\" )", Value(121*200.482 / 1936.27));
CHECK_EVAL("EUROCONVERT( 122; \"LUX\"; \"ats\" )", Value(122*13.7603 / 40.3399));
CHECK_EVAL("EUROCONVERT( 123; \"LUX\"; \"bef\" )", Value(123*40.3399 / 40.3399));
CHECK_EVAL("EUROCONVERT( 124; \"LUX\"; \"dem\" )", Value(124*1.95583 / 40.3399));
CHECK_EVAL("EUROCONVERT( 125; \"LUX\"; \"esp\" )", Value(125*166.386 / 40.3399));
CHECK_EVAL("EUROCONVERT( 126; \"LUX\"; \"eur\" )", Value(126*1 / 40.3399));
CHECK_EVAL("EUROCONVERT( 127; \"LUX\"; \"fim\" )", Value(127*5.94573 / 40.3399));
CHECK_EVAL("EUROCONVERT( 128; \"LUX\"; \"frf\" )", Value(128*6.55957 / 40.3399));
CHECK_EVAL("EUROCONVERT( 129; \"LUX\"; \"grd\" )", Value(129*340.75 / 40.3399));
CHECK_EVAL("EUROCONVERT( 130; \"LUX\"; \"iep\" )", Value(130*0.787564 / 40.3399));
CHECK_EVAL("EUROCONVERT( 131; \"LUX\"; \"itl\" )", Value(131*1936.27 / 40.3399));
CHECK_EVAL("EUROCONVERT( 132; \"LUX\"; \"nlg\" )", Value(132*2.20371 / 40.3399));
CHECK_EVAL("EUROCONVERT( 133; \"LUX\"; \"pte\" )", Value(133*200.482 / 40.3399));
CHECK_EVAL("EUROCONVERT( 134; \"NLG\"; \"ats\" )", Value(134*13.7603 / 2.20371));
CHECK_EVAL("EUROCONVERT( 135; \"NLG\"; \"bef\" )", Value(135*40.3399 / 2.20371));
CHECK_EVAL("EUROCONVERT( 136; \"NLG\"; \"dem\" )", Value(136*1.95583 / 2.20371));
CHECK_EVAL("EUROCONVERT( 137; \"NLG\"; \"esp\" )", Value(137*166.386 / 2.20371));
CHECK_EVAL("EUROCONVERT( 138; \"NLG\"; \"eur\" )", Value(138*1 / 2.20371));
CHECK_EVAL("EUROCONVERT( 139; \"NLG\"; \"fim\" )", Value(139*5.94573 / 2.20371));
CHECK_EVAL("EUROCONVERT( 140; \"NLG\"; \"frf\" )", Value(140*6.55957 / 2.20371));
CHECK_EVAL("EUROCONVERT( 141; \"NLG\"; \"grd\" )", Value(141*340.75 / 2.20371));
CHECK_EVAL("EUROCONVERT( 142; \"NLG\"; \"iep\" )", Value(142*0.787564 / 2.20371));
CHECK_EVAL("EUROCONVERT( 143; \"NLG\"; \"itl\" )", Value(143*1936.27 / 2.20371));
CHECK_EVAL("EUROCONVERT( 144; \"NLG\"; \"lux\" )", Value(144*40.3399 / 2.20371));
CHECK_EVAL("EUROCONVERT( 145; \"NLG\"; \"pte\" )", Value(145*200.482 / 2.20371));
CHECK_EVAL("EUROCONVERT( 146; \"PTE\"; \"ats\" )", Value(146*13.7603 / 200.482));
CHECK_EVAL("EUROCONVERT( 147; \"PTE\"; \"bef\" )", Value(147*40.3399 / 200.482));
CHECK_EVAL("EUROCONVERT( 148; \"PTE\"; \"dem\" )", Value(148*1.95583 / 200.482));
CHECK_EVAL("EUROCONVERT( 149; \"PTE\"; \"esp\" )", Value(149*166.386 / 200.482));
CHECK_EVAL("EUROCONVERT( 150; \"PTE\"; \"eur\" )", Value(150*1 / 200.482));
CHECK_EVAL("EUROCONVERT( 151; \"PTE\"; \"fim\" )", Value(151*5.94573 / 200.482));
CHECK_EVAL("EUROCONVERT( 152; \"PTE\"; \"frf\" )", Value(152*6.55957 / 200.482));
CHECK_EVAL("EUROCONVERT( 153; \"PTE\"; \"grd\" )", Value(153*340.75 / 200.482));
CHECK_EVAL("EUROCONVERT( 154; \"PTE\"; \"iep\" )", Value(154*0.787564 / 200.482));
CHECK_EVAL("EUROCONVERT( 155; \"PTE\"; \"itl\" )", Value(155*1936.27 / 200.482));
CHECK_EVAL("EUROCONVERT( 156; \"PTE\"; \"lux\" )", Value(156*40.3399 / 200.482));
CHECK_EVAL("EUROCONVERT( 157; \"PTE\"; \"nlg\" )", Value(157*2.20371 / 200.482));
}
// FV
void TestFinancialFunctions::testFV()
{
// ODF
CHECK_EVAL("FV(10%;12;-100;100)" , Value(1824.5855390489)); // A trivial example of FV.
}
// FVSCHEDULE
void TestFinancialFunctions::testFVSCHEDULE()
{
// ODF
CHECK_EVAL_SHORT("FVSCHEDULE(1000000; {0.03; 0.04; 0.05})" , Value(1124760)); // A trivial example of FVSCHEDULE.
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFVSCHEDULE(1000000; {0.03; 0.04; 0.05})" , Value(1124760));
}
// INTRATE
void TestFinancialFunctions::testINTRATE()
{
// ODF
CHECK_EVAL_SHORT("INTRATE( DATE(2002; 6;8); DATE(1995;10;5); 100000; 200000; 0 )" , Value::errorVALUE()); // Settlement date must be before the maturity date.
CHECK_EVAL_SHORT("INTRATE( DATE(2002; 6;8); DATE(2002; 6;8); 100000; 200000; 0 )" , Value::errorVALUE()); // Settlement date must be before the maturity date.
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 50)" , Value::errorVALUE()); // Unknown Basis returns Error.
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 0 )" , Value(0.1498127341)); // An example of INTRATE.
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000 )" , Value(0.1498127341)); // Basis defaults to 0.
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 1 )" , Value(0.1497128794)); //
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 2 )" , Value(0.1476620180)); //
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 3 )" , Value(0.1497128794)); //
CHECK_EVAL_SHORT("INTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 4 )" , Value(0.1498127341)); //
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETINTRATE( DATE(1995;10;5); DATE(2002; 6;8); 100000; 200000; 0 )" , Value(0.1498127341));
}
// IPMT
void TestFinancialFunctions::testIPMT()
{
// ODF
CHECK_EVAL_SHORT("IPMT(5%/12;10;360;100000)", Value(-412.0850243)); // An example of IPMT. The interest payment on a 100000 unit loan
// in the 10th month of a 30 year loan at 5% annual interest.
CHECK_EVAL_SHORT("IPMT(5%/12;10;360;100000;0;1)", Value(-410.3751278993)); // Payments at the beginning of each period.
// The total payment is the principle plus the interest.
CHECK_EVAL_SHORT("PPMT(5%/12;10;360;100000)+IPMT(5%/12;10;360;100000)-PMT(5%/12;360;100000)", Value(0));
}
// ISPMT
void TestFinancialFunctions::testISPMT()
{
// betersolutions
CHECK_EVAL("ISPMT(10%/12;1 ;36;8000000)", Value(-64814.8148148148)); //
CHECK_EVAL("ISPMT(10% ;1 ;3 ;8000000)", Value(-533333.3333333333)); //
// ODF
CHECK_EVAL("ISPMT(5%/12;12;360;100000)", Value(-402.7777777778)); // A trivial example of ISPMT. A 100000 unit investment with an
// annual interest rate of 5% and a 30 year term has an interest payment
// of 402.78 units in month 12.
}
// Level-coupon bond
// LEVEL_COUPON(faceValue; couponRate; couponsPerYear; years; marketRate)
void TestFinancialFunctions::testLEVELCOUPON()
{
CHECK_EVAL("LEVEL_COUPON(1000; .13; 1; 4; .1)", Value(1095.0959633904788));
CHECK_EVAL("LEVEL_COUPON(1000; .13; 2; 4; .1)", Value(1096.9481913913939));
CHECK_EVAL("LEVEL_COUPON(1000; .10; 1; 10; .25)", Value(464.4245094400000));
CHECK_EVAL("LEVEL_COUPON(1000; .12; 1; 10; .25)", Value(535.8345748480000));
CHECK_EVAL("LEVEL_COUPON(1000; .20; 1; 10; .25)", Value(821.4748364800000));
}
// MDURATION
void TestFinancialFunctions::testMDURATION()
{
CHECK_EVAL("MDURATION(\"2004-02-01\"; \"2004-05-31\"; 0.08; 0.09; 2; 0)" , Value(0.3189792663)); // These tests go over a leap year day,
// and intentionally end on May 31, which
// illustrates the differences between
// many bases
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMDURATION(\"2004-02-01\"; \"2004-05-31\"; 0.08; 0.09; 2; 0)" , Value(0.3189792663));
}
// MIRR
void TestFinancialFunctions::testMIRR()
{
// ODF
CHECK_EVAL("MIRR({100;200;-50;300;-200}; 5%; 6%)", Value(0.342823387842));
// bettersolutions.com
CHECK_EVAL("MIRR({-10;30;20;10;20};0.1;0.12)", Value(0.7712844619));
CHECK_EVAL("MIRR({-100;30;30;30;30};0.1;1)", Value(0.4564753151));
CHECK_EVAL("MIRR({-50;20;40;70};10/100;12/100)", Value(0.4090837902));
CHECK_EVAL("MIRR({-5;1;2;3;4};10/100;0.12)", Value(0.2253901556));
CHECK_EVAL("MIRR({1000;1100;1200;1500;1600};10%;12%)", Value(Value::errorDIV0()));
}
// Yearly nominal interest rate
// NOMINAL(effectiveRate, periods)
void TestFinancialFunctions::testNOMINAL()
{
CHECK_EVAL("NOMINAL(13.5%; 12)", Value(0.1273031669590416));
CHECK_EVAL("NOMINAL(13.5%; 12)", Value(0.1273031669590416));
CHECK_EVAL("NOMINAL(25%; 12)", Value(0.2252311814580734));
CHECK_EVAL("NOMINAL(25%; 4)", Value(0.2294850537622564));
CHECK_EVAL("NOMINAL(20%; 12)", Value(0.1837136459967743));
CHECK_EVAL("NOMINAL(10%; 12)", Value(0.0956896851468452));
// rate must be positive
CHECK_EVAL("NOMINAL(0; 12)", Value::errorVALUE());
// periods must be positive
CHECK_EVAL("NOMINAL(10%; 0)", Value::errorDIV0());
CHECK_EVAL("NOMINAL(10%; -1)", Value::errorVALUE());
CHECK_EVAL("NOMINAL(10%; -2)", Value::errorVALUE());
// test cases in OpenFormula specification
CHECK_EVAL("NOMINAL(8%;4)", Value(0.0777061876330940));
CHECK_EVAL("NOMINAL(12.5%;12)", Value(0.118362966638538));
CHECK_EVAL("NOMINAL(1%;2)", Value(0.00997512422417790));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETNOMINAL(8%;4)", Value(0.0777061876330940));
}
// NPER
void TestFinancialFunctions::testNPER()
{
// ODF
CHECK_EVAL_SHORT("NPER(5% ;-100;1000)", Value(14.2067)); // A trivial example of NPER.
CHECK_EVAL_SHORT("NPER(5% ;-100;1000;100)", Value(15.2067)); // A trivial example of NPER with non-zero FV.
CHECK_EVAL_SHORT("NPER(5% ;-100;1000;100;1)", Value(14.2067)); // A trivial example of NPER with non-zero FV and PayType.
CHECK_EVAL_SHORT("NPER(0 ;-100;1000)", Value(10.0000)); // TODO Rate can be zero.
CHECK_EVAL_SHORT("NPER(-1%;-100;1000)", Value(9.483283066)); // TODO Rate can be negative.
}
// Net present value
// NPV(rate, values)
void TestFinancialFunctions::testNPV()
{
CHECK_EVAL("NPV(100%; 4; 5; 7)", Value(4.125));
CHECK_EVAL("NPV(10%; 100; 200)", Value(256.198347107438));
}
// ODDLPRICE
void TestFinancialFunctions::testODDLPRICE()
{
// ODF tests. Not all tests pass, but I'm not sure if the spec or the implementation is wrong
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;100;2)", Value( 90.9975570033 ) ); //
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;100;1;0)", Value( 90.9975570033 ) ); // f=1, b=0
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;100;2;0)", Value( 90.9975570033 ) ); // f=2, b=0
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;100;4;0)", Value( 90.9975570033 ) ); // f=4, b=0
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;1;1)", Value( 102.5120875338 ) ); // f=1, b=1
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;2;1)", Value( 102.510143853 ) ); // f=2, b=1
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;4;1)", Value( 102.509884509 ) ); // f=4, b=1
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;1;2)", Value( 102.512087534 ) ); // f=1, b=2
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;2;2)", Value( 102.510143853 ) ); // f=2, b=2
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;100;4;2)", Value( 102.509884509 ) ); // f=4, b=2
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;1000;1;3)", Value( 794.575995564 ) ); // f=1, b=3
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;1000;2;3)", Value( 794.671729071 ) ); // f=2, b=3
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;5%;1000;4;3)", Value( 794.684531308 ) ); // f=4, b=3
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;1000;1;4)", Value( 932.992137337 ) ); // f=1, b=4
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;1000;2;4)", Value( 932.992137337 ) ); // f=2, b=4
CHECK_EVAL_SHORT( "ODDLPRICE(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;1.5%;1000;4;4)", Value( 932.992137337 ) ); // f=4, b=4
}
// ODDLYIELD
void TestFinancialFunctions::testODDLYIELD()
{
// ODF tests
// Basis 0
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;91;100 ;2 )", Value( 4.997775351/100.0 ) ); // Without Basis parameter
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;91;100 ;1;0)", Value( 4.997775351/100.0 ) ); // With Frequency=1 and Basis=0
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;91;100 ;2;0)", Value( 4.997775351/100.0 ) ); // With Frequency=2 and Basis=0
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;91;100 ;4;0)", Value( 4.997775351/100.0 ) ); // With Frequency=4 and Basis=0
// Basis 1
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100 ;1;1)", Value( 1.408788601/100.0 ) ); // With Frequency=1 and Basis=1
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100 ;2;1)", Value( 1.408379719/100.0 ) ); // With Frequency=2 and Basis=1
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100 ;4;1)", Value( 1.408325114/100.0 ) ); // With Frequency=4 and Basis=1
// Basis 2
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100;1;2)", Value( 1.408788601/100.0 ) ); // With Frequency=1 and Basis=2
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100 ;4;2)", Value( 1.408379719/100.0 ) ); // With Frequency=2 and Basis=2
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;103;100 ;2;2)", Value( 1.408325114/100.0 ) ); // With Frequency=4 and Basis=2
// Basis 3
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;795;1000;1;3)", Value( 4.987800402/100.0 ) ); // With Frequency=1 and Basis=3
QEXPECT_FAIL("", "Wrong?", Continue);
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;795;1000;2;3)", Value( 4.990550494/100.0 ) ); // With Frequency=2 and Basis=3
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);3%;795;1000;4;3)", Value( 4.990918451/100.0 ) ); // With Frequency=4 and Basis=3
// Basis 4
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;933;1000;1;4)", Value( 1.499836493/100.0 ) ); // With Frequency=1 and Basis=4
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;933;1000;2;4)", Value( 1.499836493/100.0 ) ); // With Frequency=2 and Basis=4
CHECK_EVAL_SHORT( "ODDLYIELD(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);2%;933;1000;4;4)", Value( 1.499836493/100.0 ) ); // With Frequency=4 and Basis=4
}
// PDURATION
void TestFinancialFunctions::testPDURATION()
{
// is DURATION in kspread
CHECK_EVAL_SHORT("PDURATION( 0.1; 10; 100 )" , Value(24.158858)); // simple use case
CHECK_EVAL_SHORT("PDURATION( 0.1; 100; 10 )" , Value(-24.158858)); // curentValue > desiredValue
CHECK_EVAL_SHORT("PDURATION( 0; 10; 11 )" , Value::errorVALUE()); // rate > 0
CHECK_EVAL_SHORT("PDURATION( 0.1; 0; 11 )" , Value::errorVALUE()); // currentValue > 0
CHECK_EVAL_SHORT("PDURATION( 0.1; 10; 0 )" , Value::errorVALUE()); // desiredValue > 0
}
// PMT
void TestFinancialFunctions::testPMT()
{
// ODF
CHECK_EVAL_SHORT("PMT(5%;12;1000)", Value(-112.8254100208)); // A trivial example of PMT.
CHECK_EVAL_SHORT("PMT(5%;12;1000;100)", Value(-119.1079510229)); // A trivial example of PMT with non-zero FV.
CHECK_EVAL_SHORT("PMT(5%;12;1000;100;1)", Value(-113.4361438313)); // A trivial example of PMT with non-zero FV and PayType.
CHECK_EVAL_SHORT("PMT(0;10;1000)", Value(-100.00000)); // TODO Rate can be zero.
}
// PPMT
void TestFinancialFunctions::testPPMT()
{
// bettersolution.com
CHECK_EVAL("PPMT(10%/12;1;24;2000)", Value(-75.6231860084)); // A simple test case
CHECK_EVAL("PPMT(8%;10;10;200000)", Value(-27598.0534624214)); // A simple test case
// ODF
CHECK_EVAL("PPMT(3%;1;12;100)", Value(-7.0462085473)); // A simple test case
CHECK_EVAL("PPMT(8%;5;24;10000;0)", Value(-203.7735140493)); // With nPer=5 and Future=0
CHECK_EVAL("PPMT(8%;10;24;10000;2000)", Value(-359.2921746011)); // With nPer=10 and Future=2000
CHECK_EVAL("PPMT(8%;10;24;10000;2000;1)", Value(-332.6779394454)); // With Type=1
// these tests seems to be wrong in specs. remove superfluous parameter "1".
CHECK_EVAL("PPMT(3%;1;12;100;200)", Value(-21.1386256419)); // With future value
CHECK_EVAL("PPMT(3%;1;12;100;200;1)", Value(-20.5229375164)); // With future value and type
}
// PRICEMAT
void TestFinancialFunctions::testPRICEMAT()
{
// ODF - TODO expand to 10 signif.
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5% )", Value(103.819218241)); // Without Basis parameter
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5%;1)", Value(103.824693325)); // With Basis=1 specs 103.824693325
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5%;2)", Value(103.858482159)); // With Basis=2
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5%;3)", Value(103.824693325)); // With Basis=3
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5%;4)", Value(103.819218241)); // With Basis=4
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1992;12;31);DATE(1990;1;1);3%;2%;0)", Value(102.395007924)); //
CHECK_EVAL_SHORT("PRICEMAT(DATE(1990;6;1);DATE(1992;12;31);DATE(1990;1;1);5%;3%;2)", Value(104.709020052)); //
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICEMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1);6%;5% )", Value(103.819218241));
}
// PV
void TestFinancialFunctions::testPV()
{
// TODO check type > 1, check div0
// ODF
CHECK_EVAL_SHORT("PV(10%;12;-100;100)", Value(649.5061005186)); // A trivial example of PV.
}
// PV_ANNUITY
void TestFinancialFunctions::testPV_ANNUITY()
{
// kspread
CHECK_EVAL_SHORT("PV_ANNUITY(1000;0.05;5)", Value(4329.47667063));
}
// RATE
void TestFinancialFunctions::testRATE()
{
CHECK_EVAL_SHORT("RATE(4*12;-200;8000)", Value(0.00770147));
}
// RECEIVED
void TestFinancialFunctions::testRECEIVED()
{
// ODF
CHECK_EVAL_SHORT("RECEIVED(DATE(1990;6;1);DATE(1990;12;31);10000;5%)" , Value(10300.4291845494)); // Without Basis parameter
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRECEIVED(DATE(1990;6;1);DATE(1990;12;31);10000;5%)" , Value(10300.4291845494));
}
// RRI
void TestFinancialFunctions::testRRI()
{
CHECK_EVAL_SHORT("RRI(1;100;200)" , Value(1)); // A trivial example of RRI.
CHECK_EVAL_SHORT("RRI(12;5000;10000)" , Value(0.05946309436)); // RRI, practical example
CHECK_EVAL_SHORT("RRI(12;10000;5000)" , Value(-0.056125687)); // If future value is less than present value, resultant rate is negative
CHECK_EVAL_SHORT("RRI(0;100;200)" , Value(Value::errorVALUE())); // N must be greater than 0.
}
// Straight-line depreciation
// SLN(cost, salvage, life)
void TestFinancialFunctions::testSLN()
{
// Excel example: http://office.microsoft.com/en-us/excel/HP100623811033.aspx
CHECK_EVAL("SLN(30000; 7500; 10)", Value(2250.0));
// http://www.vni.com/products/imsl/jmsl/v30/api/com/imsl/finance/slnEx1.html
CHECK_EVAL("SLN(2500; 500; 24)", Value(83.3333333333333));
// http://www.gnome.org/projects/gnumeric/doc/gnumeric-SLN.shtml
CHECK_EVAL("SLN(10000; 700; 10)", Value(930));
// test cases in OpenFormula specification
CHECK_EVAL("SLN(4000;500;4)", Value(875));
}
// Sum-of-years' digits depreciation
// SYD(cost, salvage, life, period)
void TestFinancialFunctions::testSYD()
{
// Excel example: http://office.microsoft.com/en-us/excel/HP100623821033.aspx
CHECK_EVAL("SYD(30000; 7500; 10; 1)", Value(4090.909090909090));
CHECK_EVAL("SYD(30000; 7500; 10; 10)", Value(409.0909090909090));
// http://www.vni.com/products/imsl/jmsl/v30/api/com/imsl/finance/sydEx1.html
CHECK_EVAL("SYD(25000; 5000; 15; 14)", Value(333.3333333333333));
// http://www.gnome.org/projects/gnumeric/doc/gnumeric-SYD.shtml
CHECK_EVAL("SYD(5000; 200; 5; 2)", Value(1280));
// test cases in OpenFormula specification
CHECK_EVAL("SYD(4000;500;4;2)", Value(1050));
}
// TBILLEQ
void TestFinancialFunctions::testTBILLEQ()
{
// TODO check function, check OOo-2.2.1
// ODF
CHECK_EVAL("TBILLEQ(DATE(1996;01;01);DATE(1996;02;01);5%)", Value(0.0509136560)); //
CHECK_EVAL("TBILLEQ(DATE(1995;12;31);DATE(1996;02;01);5%)", Value(0.0509207589)); // specs 0.050920759
CHECK_EVAL("TBILLEQ(DATE(1995;12;31);DATE(1996;07;01);5%)", Value(0.0520091194)); // specs 0.052016531
CHECK_EVAL("TBILLEQ(DATE(1995;12;31);DATE(1996;12;31);5%)", Value(Value::errorVALUE())); // specs 0.053409423 OOo-2.2.1 Error(#VALUE!) 361 days
CHECK_EVAL("TBILLEQ(DATE(1996;01;01);DATE(1996;06;30);5%)", Value(0.0519943020)); // specs 0.052001710
CHECK_EVAL("TBILLEQ(DATE(1996;01;01);DATE(1996;07;01);5%)", Value(0.0520017096)); // specs 0.052009119
CHECK_EVAL("TBILLEQ(DATE(1996;01;01);DATE(1996;12;31);5%)", Value(0.0533625731)); // specs 0.053401609
CHECK_EVAL("TBILLEQ(DATE(1996;01;01);DATE(1997;01;01);5%)", Value(Value::errorVALUE())); // specs 0.053409423 OOo-2.2.1 Error(#VALUE!) days 361
CHECK_EVAL("TBILLEQ(DATE(1996;07;01);DATE(1997;07;01);5%)", Value(Value::errorVALUE())); // specs 0.053401609 OOo-2.2.1 Error(#VALUE!) days 361
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLEQ(DATE(1996;01;01);DATE(1996;02;01);5%)", Value(0.0509136560));
}
// TBILLPRICE
void TestFinancialFunctions::testTBILLPRICE()
{
// ODF
CHECK_EVAL("TBILLPRICE(DATE(1996;01;01);DATE(1996;02;01);5%)", Value(99.5694444444)); //
CHECK_EVAL("TBILLPRICE(DATE(1995;12;31);DATE(1996;02;01);5%)", Value(99.5555555555)); //
CHECK_EVAL("TBILLPRICE(DATE(1995;12;31);DATE(1996;07;01);5%)", Value(97.4722222222)); // ODF specs 97.45833333 OOo-2.2.1 97.47222222
CHECK_EVAL("TBILLPRICE(DATE(1995;12;31);DATE(1996;12;31);5%)", Value(94.9861111111)); // ODF specs 94.91666667 OOo-2.2.1 94.98611111
CHECK_EVAL("TBILLPRICE(DATE(1996;01;01);DATE(1996;06;30);5%)", Value(97.5000000000)); // ODF specs 97.48611111 OOo-2.2.1 97.50000000
CHECK_EVAL("TBILLPRICE(DATE(1996;01;01);DATE(1996;07;01);5%)", Value(97.4861111111)); // ODF specs 97.47222222 OOo-2.2.1 97.48611111
CHECK_EVAL("TBILLPRICE(DATE(1996;01;01);DATE(1996;12;31);5%)", Value(Value::errorVALUE())); // ODF specs 94.93055556 OOo-2.2.1 Err:502
CHECK_EVAL("TBILLPRICE(DATE(1996;01;01);DATE(1997;01;01);5%)", Value(94.9861111111)); // ODF specs 94.91666667 OOo-2.2.1 94.98611111
CHECK_EVAL("TBILLPRICE(DATE(1996;07;01);DATE(1997;07;01);5%)", Value(94.9861111111)); // ODF specs 94.93055556 OOo-2.2.1 94.98611111
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLPRICE(DATE(1996;01;01);DATE(1996;02;01);5%)", Value(99.5694444444));
}
// TBILLYIELD
void TestFinancialFunctions::testTBILLYIELD()
{
// ODF tests. All results are taken from OOo-2.2.1 instead of results from ODF-specs
// new implementation uses day360(US) to get daydiff. TODO check if we should test against 361 days instead 360 in function to get Error away
CHECK_EVAL("TBILLYIELD(DATE(1996;01;01);DATE(1996;02;01);99.57)", Value(0.0501511337)); //
CHECK_EVAL("TBILLYIELD(DATE(1995;12;31);DATE(1996;02;01);99.56)", Value(0.0497187626)); //
CHECK_EVAL("TBILLYIELD(DATE(1995;12;31);DATE(1996;07;01);97.46)", Value(0.0515511576)); // specs 0.0512695
CHECK_EVAL("TBILLYIELD(DATE(1995;12;31);DATE(1996;12;31);94.92)", Value(Value::errorVALUE())); // specs 0.0526414 OOo-2.2.1 Error(#VALUE!)
CHECK_EVAL("TBILLYIELD(DATE(1996;01;01);DATE(1996;06;30);97.49)", Value(0.0514924608)); // specs 0.0512080
CHECK_EVAL("TBILLYIELD(DATE(1996;01;01);DATE(1996;07;01);97.47)", Value(0.0516265948)); // specs 0.0513429
CHECK_EVAL("TBILLYIELD(DATE(1996;01;01);DATE(1996;12;31);94.93)", Value(Value::errorVALUE())); // specs 0.0526762 OOo-2.2.1 Error(#VALUE!)
CHECK_EVAL("TBILLYIELD(DATE(1996;01;01);DATE(1997;01;01);94.92)", Value(Value::errorVALUE())); // specs 0.0526414 OOo-2.2.1 Error(#VALUE!)
CHECK_EVAL("TBILLYIELD(DATE(1996;07;01);DATE(1997;07;01);94.93)", Value(Value::errorVALUE())); // specs 0.0526762 OOo-2.2.1 Error(#VALUE!)
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLYIELD(DATE(1996;01;01);DATE(1996;02;01);99.57)", Value(0.0501511337));
}
// VDB
void TestFinancialFunctions::testVDB()
{
// ODF
CHECK_EVAL("VDB(10000;600;10;0 ;0.875;1.5)", Value(1312.50));
CHECK_EVAL("VDB(10000;600;10;0.875;1.875;1.5)", Value(1303.125));
CHECK_EVAL("VDB(10000;600;10;1.875;2.875;1.5)", Value(1107.65625));
CHECK_EVAL("VDB(10000;600;10;2.875;3.875;1.5)", Value(941.5078125));
CHECK_EVAL("VDB(10000;600;10;3.875;4.875;1.5)", Value(800.2816406250));
CHECK_EVAL("VDB(10000;600;10;4.875;5.875;1.5)", Value(767.7910823171));
CHECK_EVAL("VDB(10000;600;10;5.875;6.875;1.5)", Value(767.410625));
CHECK_EVAL("VDB(10000;600;10;6.875;7.875;1.5)", Value(767.410625));
CHECK_EVAL("VDB(10000;600;10;7.875;8.875;1.5)", Value(767.410625));
CHECK_EVAL("VDB(10000;600;10;8.875;9.875;1.5)", Value(767.410625));
CHECK_EVAL("VDB(10000;600;10;9.875;10 ;1.5)", Value(95.9263281250));
}
// XIRR
void TestFinancialFunctions::testXIRR()
{
// ODF
CHECK_EVAL_SHORT("XIRR( {-20000;4000;12000;8000}; {date(2000;01;01); date(2000;06;01); date(2000;12;30); date(2001;03;01)} )", Value(0.2115964)); //
CHECK_EVAL_SHORT("XIRR( {-20000;25000}; {date(2000;01;01); date(2001;01;01)} )", Value(0.2492381)); //
CHECK_EVAL_SHORT("XIRR( {-10000;4000;12000}; {date(2000;01;01); date(2002;06;01); date(2004;01;01)} )", Value(0.1405418)); //
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXIRR( {-20000;25000};{date(2000;01;01); date(2001;01;01)} )", Value(0.2492381));
}
// XNPV
void TestFinancialFunctions::testXNPV()
{
// bettersolution.com
CHECK_EVAL("XNPV(0.1; {-1000;2000;3000}; {date(2005;01;01); date(2005;01;10); date(2005;01;15)})" , Value(3984.3581140636)); //
// with dates {date(2005;01;01); date(2005;03;01); date(2005;10;30); date(2006;02;15)}
CHECK_EVAL("XNPV(0.09; {-10000;2750;4250;3250}; {38353;38412;38655;38763})", Value(-380.3891178530)); //
CHECK_EVAL("XNPV(30; {-10000;2750;4250;3250}; {38353;38412;38655;38763})", Value(-8104.7862519770)); //
CHECK_EVAL("XNPV(-30; {-10000;2750;4250;3250}; {38353;38412;38655;38763})", Value(Value::errorNUM())); //
CHECK_EVAL("XNPV(0.09; {-10000;2750}; {date(2005;01;01); date(2005;01;10); date(2005;01;15)})", Value(Value::errorNUM())); //
CHECK_EVAL("XNPV(0.1; {-1000;2000;3000}; {\"fail\"; date(2005;01;10); date(2005;01;15)})", Value(Value::errorVALUE())); //
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXNPV(0.09; {-10000;2750;4250;3250}; {38353;38412;38655;38763})",Value(-380.3891178530));
}
// YIELDDISC
void TestFinancialFunctions::testYIELDDISC()
{
// ODF
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;12;31);941.66667;1000 )", Value(0.1061946838)); // Without Basis parameter
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;12;31);941.64384;1000; 1)", Value(0.1061972566)); // With Basis=1 specs 0.106238821 OOo-2.2.1 0.1061972566
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;12;31);940.83333;1000; 2)", Value(0.1062887575)); // With Basis=2 specs 0.107807168 OOo-2.2.1 0.1062887575
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;12;31);941.64384;1000; 3)", Value(0.1061972566)); // With Basis=3 specs 0.106238821 OOo-2.2.1 0.1061972566
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;12;31);941.94444;1000; 4)", Value(0.1061633823)); // With Basis=4 specs 0.105657842 OOo-2.2.1 0.1061633823
CHECK_EVAL("YIELDDISC(DATE(1990;01;01);DATE(1990;12;31);97.08219;100; 1)", Value(0.0301376180)); // specs 0.051522942 OOo-2.2.1 0.0301376180
CHECK_EVAL("YIELDDISC(DATE(1990;06;01);DATE(1990;06;30);99.75833;100; 4)", Value(0.0300730914)); //
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDDISC(DATE(1990;06;01);DATE(1990;12;31);941.66667;1000 )", Value(0.1061946838));
}
// YIELDMAT
void TestFinancialFunctions::testYIELDMAT()
{
// ODF
CHECK_EVAL_SHORT("YIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 6%;103.819218241 )", Value(0.050000000)); // Without Basis parameter
CHECK_EVAL_SHORT("YIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 6%;103.824693325;1)", Value(0.050000000)); // With Basis=1
// CHECK_EVAL_SHORT( "YIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 6%;103.858482159;2)", Value( 0.050000000 ) ); // With Basis=2
CHECK_EVAL_SHORT("YIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 6%;103.824693325;3)", Value(0.050000000)); // With Basis=3
// CHECK_EVAL_SHORT( "YIELDMAT(DATE(1990;6;1);DATE(1992;12;31);DATE(1990;1;1); 6%;103.817732653;4)", Value( 0.050000000 ) ); // With Basis=4 NOK diff = 0.0074805
CHECK_EVAL_SHORT("YIELDMAT(DATE(1990;6;1);DATE(1992;12;31);DATE(1990;1;1); 3%;102.395007924;0)", Value(0.020000000)); // With Basis=0
// CHECK_EVAL_SHORT( "YIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 5%;102.967175933;2)", Value( 0.030000000 ) ); // With Basis=2 NOK diff = -0.0126036
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDMAT(DATE(1990;6;1);DATE(1995;12;31);DATE(1990;1;1); 6%;103.819218241 )", Value(0.050000000));
}
// Zero-coupon (pure discount) bond
// ZERO_COUPON(faceValue; rate; years)
void TestFinancialFunctions::testZEROCOUPON()
{
CHECK_EVAL("ZERO_COUPON(1000;.1;20)", Value(148.6436280241434531));
CHECK_EVAL("ZERO_COUPON(1000;.2;20)", Value(26.0840533045888456));
CHECK_EVAL("ZERO_COUPON(1000;.15/12;10)", Value(883.1809261539680165));
CHECK_EVAL("ZERO_COUPON(1000;.25;1)", Value(800));
}
-
-QTEST_KDEMAIN(TestFinancialFunctions, GUI)
+QTEST_MAIN(TestFinancialFunctions)
diff --git a/sheets/tests/TestFinancialFunctions.h b/sheets/tests/TestFinancialFunctions.h
index 15fa35c26c4..6401b0527e0 100644
--- a/sheets/tests/TestFinancialFunctions.h
+++ b/sheets/tests/TestFinancialFunctions.h
@@ -1,113 +1,112 @@
/* This file is part of the KDE project
Copyright 2006 Ariya Hidayat <ariya@kde.org>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_FINANCIAL_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_FINANCIAL_FUNCTIONS
-#include <QtGui>
-#include <qtest_kde.h>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestFinancialFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testACCRINT();
void testACCRINTM();
void testAMORDEGRC();
void testAMORLINC();
void testCOMPOUND(); // no ODF tests available
void testCONTINUOUS(); // no ODF tests available
void testCOUPDAYBS();
void testCOUPDAYS();
void testCOUPDAYSNC();
void testCOUPNCD();
void testCOUPNUM();
void testCOUPPCD();
void testCUMIPMT();
void testCUMPRINC();
void testDB();
void testDDB();
void testDISC();
void testDOLLARDE();
void testDOLLARFR();
void testDURATION();
void testDURATION_ADD(); // excel
void testEFFECT();
void testEURO();
void testEUROCONVERT();
void testFV();
// void testFV_ANNUITY(); // k
void testFVSCHEDULE();
void testINTRATE();
void testIPMT();
// void testIRR(); // to be implemented
void testISPMT();
void testLEVELCOUPON();
void testMDURATION();
void testMIRR();
void testNOMINAL();
void testNPER();
void testNPV();
// void testODDFPRICE(); // to be implemented
// void testODDFYIELD(); // to be implemented
void testODDLPRICE();
void testODDLYIELD();
void testPDURATION();
void testPMT();
void testPPMT();
// void testPRICE(); // to be implemented
// void testPRICEDISC(); // to be implemented
void testPRICEMAT();
void testPV();
void testPV_ANNUITY(); // no ODF test available
void testRATE();
void testRECEIVED();
void testRRI();
void testSLN();
void testSYD();
void testTBILLEQ();
void testTBILLPRICE();
void testTBILLYIELD();
void testVDB();
void testXIRR();
void testXNPV();
// void testYIELD(); // to be implemented
void testYIELDDISC();
void testYIELDMAT();
void testZEROCOUPON();
// private:
// Value evaluate(const QString&);
// Value evaluateShort(const QString&);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_FINANCIAL_FUNCTIONS
diff --git a/sheets/tests/TestFormula.cpp b/sheets/tests/TestFormula.cpp
index de9b31ea2c0..eab17ed3c8a 100644
--- a/sheets/tests/TestFormula.cpp
+++ b/sheets/tests/TestFormula.cpp
@@ -1,371 +1,371 @@
/* This file is part of the KDE project
Copyright 2004,2007 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestFormula.h"
#include <klocale.h>
#include "TestKspreadCommon.h"
using namespace Calligra::Sheets;
static char encodeTokenType(const Token& token)
{
char result = '?';
switch (token.type()) {
case Token::Boolean: result = 'b'; break;
case Token::Integer: result = 'i'; break;
case Token::Float: result = 'f'; break;
case Token::Operator: result = 'o'; break;
case Token::Cell: result = 'c'; break;
case Token::Range: result = 'r'; break;
case Token::Identifier: result = 'x'; break;
default: break;
}
return result;
}
#if 0 // not used?
static QString describeTokenCodes(const QString& tokenCodes)
{
QString result;
if (tokenCodes.isEmpty())
result = "(invalid)";
else
for (int i = 0; i < tokenCodes.length(); i++) {
switch (tokenCodes[i].unicode()) {
case 'b': result.append("Boolean"); break;
case 'i': result.append("integer"); break;
case 'f': result.append("float"); break;
case 'o': result.append("operator"); break;
case 'c': result.append("cell"); break;
case 'r': result.append("range"); break;
case 'x': result.append("identifier"); break;
default: result.append("unknown"); break;
}
if (i < tokenCodes.length() - 1) result.append(", ");
}
return result.prepend("{").append("}");
}
#endif
#define CHECK_TOKENIZE(x,y) QCOMPARE(tokenizeFormula(x), QString(y))
static QString tokenizeFormula(const QString& formula)
{
Formula f;
QString expr = formula;
expr.prepend('=');
f.setExpression(expr);
Tokens tokens = f.tokens();
QString resultCodes;
if (tokens.valid())
for (int i = 0; i < tokens.count(); i++)
resultCodes.append(encodeTokenType(tokens[i]));
return resultCodes;
}
// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x,y) { Value z(y); QCOMPARE(evaluate(x,z),(z)); }
Value TestFormula::evaluate(const QString& formula, Value& ex)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
void TestFormula::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
void TestFormula::testTokenizer()
{
// simple, single-token formulas
CHECK_TOKENIZE("True", "x");
CHECK_TOKENIZE("False", "x");
CHECK_TOKENIZE("36", "i");
CHECK_TOKENIZE("0", "i");
CHECK_TOKENIZE("3.14159", "f");
CHECK_TOKENIZE(".25", "f");
CHECK_TOKENIZE("1e-9", "f");
CHECK_TOKENIZE("2e3", "f");
CHECK_TOKENIZE(".3333e0", "f");
// cell/range/identifier
CHECK_TOKENIZE("A1", "c");
CHECK_TOKENIZE("Sheet1!A1", "c");
CHECK_TOKENIZE("'Sheet1'!A1", "c");
CHECK_TOKENIZE("'Sheet One'!A1", "c");
CHECK_TOKENIZE("2006!A1", "c");
CHECK_TOKENIZE("2006bak!A1", "c");
CHECK_TOKENIZE("2006bak2!A1", "c");
CHECK_TOKENIZE("'2006bak2'!A1", "c");
CHECK_TOKENIZE("A1:B100", "r");
CHECK_TOKENIZE("Sheet1!A1:B100", "r");
CHECK_TOKENIZE("'Sheet One'!A1:B100", "r");
CHECK_TOKENIZE("SIN", "x");
// log2 and log10 are cell references and function identifiers
CHECK_TOKENIZE("LOG2", "c");
CHECK_TOKENIZE("LOG10:11", "r");
CHECK_TOKENIZE("LOG2(2)", "xoio");
CHECK_TOKENIZE("LOG10(10)", "xoio");
// operators
CHECK_TOKENIZE("+", "o");
CHECK_TOKENIZE("-", "o");
CHECK_TOKENIZE("*", "o");
CHECK_TOKENIZE("/", "o");
CHECK_TOKENIZE("+", "o");
CHECK_TOKENIZE("^", "o");
CHECK_TOKENIZE("(", "o");
CHECK_TOKENIZE(")", "o");
CHECK_TOKENIZE(",", "o");
CHECK_TOKENIZE(";", "o");
CHECK_TOKENIZE("=", "o");
CHECK_TOKENIZE("<", "o");
CHECK_TOKENIZE(">", "o");
CHECK_TOKENIZE("<=", "o");
CHECK_TOKENIZE(">=", "o");
CHECK_TOKENIZE("%", "o");
// commonly used formulas
CHECK_TOKENIZE("A1+A2", "coc");
CHECK_TOKENIZE("2.5*B1", "foc");
CHECK_TOKENIZE("SUM(A1:Z10)", "xoro");
CHECK_TOKENIZE("MAX(Sheet1!Sales)", "xoro");
CHECK_TOKENIZE("-ABS(A1)", "oxoco");
// should be correctly parsed though they are nonsense (can't be evaluated)
CHECK_TOKENIZE("0E0.5", "ff");
CHECK_TOKENIZE("B3 D4:D5 Sheet1!K1", "crc");
CHECK_TOKENIZE("SIN A1", "xc");
CHECK_TOKENIZE("SIN A1:A20", "xr");
// invalid formulas, can't be parsed correctly
CHECK_TOKENIZE("+1.23E", QString());
// incomplete formulas
CHECK_TOKENIZE("COMPARE(\"", "xo");
CHECK_TOKENIZE("SHEETS(Sheet2!", "");
// empty parameter
CHECK_TOKENIZE("IF(A1;A2;)", "xococoo");
CHECK_TOKENIZE("OFFSET(Sheet2'!B7;0;0)", "");
// function cascade
CHECK_TOKENIZE("SUM(ABS(-1);ABS(-1))", "xoxooiooxooioo");
}
void TestFormula::testConstant()
{
// simple constants
CHECK_EVAL("0", Value(0));
CHECK_EVAL("1", Value(1));
CHECK_EVAL("-1", Value(-1));
CHECK_EVAL("3.14e7", Value(3.14e7));
CHECK_EVAL("3.14e-7", Value(3.14e-7));
// String constants (from Odf 1.2 spec)
CHECK_EVAL("\"Hi\"", Value("Hi"));
CHECK_EVAL("\"Hi\"\"t\"", Value("Hi\"t"));
CHECK_EVAL("\"\\n\"", Value("\\n"));
// Constant errors
CHECK_EVAL("#N/A", Value::errorNA());
CHECK_EVAL("#DIV/0!", Value::errorDIV0());
CHECK_EVAL("#NAME?", Value::errorNAME());
CHECK_EVAL("#NULL!", Value::errorNULL());
CHECK_EVAL("#NUM!", Value::errorNUM());
CHECK_EVAL("#REF!", Value::errorREF());
CHECK_EVAL("#VALUE!", Value::errorVALUE());
}
void TestFormula::testWhitespace()
{
CHECK_EVAL("=ROUND( 10.1 ; 0 )", Value(10));
CHECK_EVAL("= ROUND(10.1;0)", Value(10));
CHECK_EVAL(" =ROUND(10.1;0)", Value::errorPARSE());
CHECK_EVAL("= ( ROUND( 9.8 ; 0 ) + ROUND( 9.8 ; 0 ) ) ", Value(20));
CHECK_EVAL("=(ROUND(9.8;0) ROUND(9.8;0))", Value::errorPARSE());
}
void TestFormula::testInvalid()
{
// Basic operations always throw errors if one of the values
// is invalid. This is the difference to SUM and co.
CHECK_EVAL("a+0", Value::errorVALUE());
CHECK_EVAL("0-z", Value::errorVALUE());
CHECK_EVAL("a*b", Value::errorVALUE());
CHECK_EVAL("u/2", Value::errorVALUE());
}
void TestFormula::testUnary()
{
// unary minus
CHECK_EVAL("-1", Value(-1));
CHECK_EVAL("--1", Value(1));
CHECK_EVAL("---1", Value(-1));
CHECK_EVAL("----1", Value(1));
CHECK_EVAL("-----1", Value(-1));
CHECK_EVAL("5-1", Value(4));
CHECK_EVAL("5--1", Value(6));
CHECK_EVAL("5---1", Value(4));
CHECK_EVAL("5----1", Value(6));
CHECK_EVAL("5-----1", Value(4));
CHECK_EVAL("5-----1*2.5", Value(2.5));
CHECK_EVAL("5------1*2.5", Value(7.5));
CHECK_EVAL("-SIN(0)", Value(0));
CHECK_EVAL("1.1-SIN(0)", Value(1.1));
CHECK_EVAL("1.2--SIN(0)", Value(1.2));
CHECK_EVAL("1.3---SIN(0)", Value(1.3));
CHECK_EVAL("-COS(0)", Value(-1));
CHECK_EVAL("1.1-COS(0)", Value(0.1));
CHECK_EVAL("1.2--COS(0)", Value(2.2));
CHECK_EVAL("1.3---COS(0)", Value(0.3));
}
void TestFormula::testBinary()
{
// simple binary operation
CHECK_EVAL("0+0", Value(0));
CHECK_EVAL("1+1", Value(2));
// power operator is left associative
CHECK_EVAL("2^3", Value(8));
CHECK_EVAL("2^3^2", Value(64));
// lead to division by zero
CHECK_EVAL("0/0", Value::errorDIV0());
CHECK_EVAL("1/0", Value::errorDIV0());
CHECK_EVAL("-4/0", Value::errorDIV0());
CHECK_EVAL("(2*3)/(6-2*3)", Value::errorDIV0());
CHECK_EVAL("1e3+7/0", Value::errorDIV0());
CHECK_EVAL("2^(99/0)", Value::errorDIV0());
}
void TestFormula::testOperators()
{
// no parentheses, checking operator precendences
CHECK_EVAL("14+3*77", Value(245));
CHECK_EVAL("14-3*77", Value(-217));
CHECK_EVAL("26*4+81", Value(185));
CHECK_EVAL("26*4-81", Value(23));
CHECK_EVAL("30-45/3", Value(15));
CHECK_EVAL("45+45/3", Value(60));
CHECK_EVAL("4+3*2-1", Value(9));
}
void TestFormula::testComparison()
{
// compare numbers
CHECK_EVAL("6>5", Value(true));
CHECK_EVAL("6<5", Value(false));
CHECK_EVAL("2=2", Value(true));
CHECK_EVAL("2=22", Value(false));
CHECK_EVAL("=3=3.0001", Value(false));
// compare booleans
CHECK_EVAL("=TRUE()=FALSE()", Value(false));
CHECK_EVAL("=TRUE()=TRUE()", Value(true));
CHECK_EVAL("=FALSE()=FALSE()", Value(true));
// compare strings
CHECK_EVAL("=\"Hi\"=\"Bye\"", Value(false));
CHECK_EVAL("=\"5\"=5", Value(false));
CHECK_EVAL("=\"Hi\"=\"HI\"", Value(false));
CHECK_EVAL("b>a", Value(true));
CHECK_EVAL("b<aa", Value(false));
CHECK_EVAL("c<d", Value(true));
CHECK_EVAL("cc>d", Value(false));
// compare dates
CHECK_EVAL("=DATE(2001;12;12)>DATE(2001;12;11)", Value(true));
CHECK_EVAL("=DATE(2001;12;12)<DATE(2001;12;11)", Value(false));
CHECK_EVAL("=DATE(1999;01;01)=DATE(1999;01;01)", Value(true));
CHECK_EVAL("=DATE(1998;01;01)=DATE(1999;01;01)", Value(false));
// errors cannot be compared
CHECK_EVAL("=NA()=NA()", Value::errorNA());
CHECK_EVAL("=NA()>NA()", Value::errorNA());
CHECK_EVAL("#DIV/0!>0", Value::errorDIV0());
CHECK_EVAL("5<#VALUE!", Value::errorVALUE());
CHECK_EVAL("#DIV/0!=#DIV/0!", Value::errorDIV0());
}
void TestFormula::testString()
{
// string expansion ...
CHECK_EVAL("\"2\"+5", Value(7));
CHECK_EVAL("2+\"5\"", Value(7));
CHECK_EVAL("\"2\"+\"5\"", Value(7));
}
void TestFormula::testFunction()
{
// function with no arguments
CHECK_EVAL("TRUE()", Value(true));
//the built-in sine function
CHECK_EVAL("SIN(0)", Value(0));
CHECK_EVAL("2+sin(\"2\"-\"2\")", Value(2));
CHECK_EVAL("\"1\"+sin(\"0\")", Value(1));
// function cascades
CHECK_EVAL("SUM(ABS( 1);ABS( 1))", Value(2));
CHECK_EVAL("SUM(ABS( 1);ABS(-1))", Value(2));
CHECK_EVAL("SUM(ABS(-1);ABS( 1))", Value(2));
CHECK_EVAL("SUM(ABS(-1);ABS(-1))", Value(2));
CHECK_EVAL("SUM(SUM(-2;-2;-2);SUM(-2;-2;-2;-2);SUM(-2;-2;-2;-2;-2))", Value(-24));
}
void TestFormula::testInlineArrays()
{
#ifdef CALLIGRA_SHEETS_INLINE_ARRAYS
// inline arrays
CHECK_TOKENIZE("{1;2|3;4}", "oioioioio");
Value array(Value::Array);
array.setElement(0, 0, Value((int)1));
array.setElement(1, 0, Value((int)2));
array.setElement(0, 1, Value((int)3));
array.setElement(1, 1, Value((int)4));
CHECK_EVAL("={1;2|3;4}", array);
array.setElement(1, 0, Value(0.0));
CHECK_EVAL("={1;SIN(0)|3;4}", array); // "dynamic"
CHECK_EVAL("=SUM({1;2|3;4})", Value(10));
#endif
}
-QTEST_KDEMAIN(TestFormula, GUI)
+QTEST_MAIN(TestFormula)
diff --git a/sheets/tests/TestFormula.h b/sheets/tests/TestFormula.h
index 7dda863c2af..39e4681bb29 100644
--- a/sheets/tests/TestFormula.h
+++ b/sheets/tests/TestFormula.h
@@ -1,57 +1,56 @@
/* This file is part of the KDE project
Copyright 2004,2007 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_FORMULA
#define CALLIGRA_SHEETS_TEST_FORMULA
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestFormula: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testTokenizer();
void testConstant();
void testWhitespace();
void testInvalid();
void testUnary();
void testBinary();
void testOperators();
void testComparison();
void testString();
void testFunction();
void testInlineArrays();
private:
Value evaluate(const QString&, Value&);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_FORMULA
diff --git a/sheets/tests/TestInformationFunctions.cpp b/sheets/tests/TestInformationFunctions.cpp
index 5a5631f9936..7a8a4fdfe8d 100644
--- a/sheets/tests/TestInformationFunctions.cpp
+++ b/sheets/tests/TestInformationFunctions.cpp
@@ -1,623 +1,623 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <CellStorage.h>
#include <Formula.h>
#include <Map.h>
#include <Sheet.h>
#include <CalculationSettings.h>
#include "TestKspreadCommon.h"
#include "TestInformationFunctions.h"
// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x,y) { Value z(y); QCOMPARE(evaluate(x,z),(z)); }
Value TestInformationFunctions::evaluate(const QString& formula, Value& ex, const Cell &cell)
{
Sheet* sheet = m_map->sheet(0);
Formula f(sheet, cell);
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
void TestInformationFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
m_map = new Map(0 /* no Doc */);
m_map->addNewSheet();
Sheet* sheet = m_map->sheet(0);
sheet->setSheetName("Sheet1");
CellStorage* storage = sheet->cellStorage();
//
// Test case data set
//
// A19:A31
storage->setValue(1,19, Value( 1 ) );
storage->setValue(1,20, Value( 2 ) );
storage->setValue(1,21, Value( 4 ) );
storage->setValue(1,22, Value( 8 ) );
storage->setValue(1,23, Value( 16 ) );
storage->setValue(1,24, Value( 32 ) );
storage->setValue(1,25, Value( 64 ) );
storage->setValue(1,26, Value( 128 ) );
storage->setValue(1,27, Value( 256 ) );
storage->setValue(1,28, Value( 512 ) );
storage->setValue(1,29, Value( 1024 ) );
storage->setValue(1,30, Value( 2048 ) );
storage->setValue(1,31, Value( 4096 ) );
// B1:B2
Formula formula(sheet);
formula.setExpression("=SUM(A19:A31)");
storage->setFormula(2,1, formula);
storage->setFormula(2,2, Formula::empty());
// B3:B17
storage->setValue(2, 3, Value("7"));
storage->setValue(2, 4, Value(2));
storage->setValue(2, 5, Value(3));
storage->setValue(2, 6, Value(true));
storage->setValue(2, 7, Value("Hello"));
// B8 leave empty
storage->setValue(2, 9, Value::errorDIV0());
storage->setValue(2, 10, Value(0));
// storage->setValue(2,11, Value( 3 ) );
// storage->setValue(2,12, Value( 4 ) );
// storage->setValue(2,13, Value( "2005-0131T01:00:00" ));
// storage->setValue(2,14, Value( 1 ) );
// storage->setValue(2,15, Value( 2 ) );
// storage->setValue(2,16, Value( 3 ) );
// storage->setValue(2,17, Value( 4 ) );
//
//
// // C4:C7
storage->setValue(3, 4, Value(4));
storage->setValue(3, 5, Value(5));
// storage->setValue(3, 6, Value( 7 ) );
storage->setValue(3, 7, Value("2005-01-31"));
// C11:C17
storage->setValue(3,11, Value( 5 ) );
storage->setValue(3,12, Value( 6 ) );
storage->setValue(3,13, Value( 8 ) );
storage->setValue(3,14, Value( 4 ) );
storage->setValue(3,15, Value( 3 ) );
storage->setValue(3,16, Value( 2 ) );
storage->setValue(3,17, Value( 1 ) );
// // C19:C31
// storage->setValue(3,19, Value( 0 ) );
// storage->setValue(3,20, Value( 5 ) );
// storage->setValue(3,21, Value( 2 ) );
// storage->setValue(3,22, Value( 5 ) );
// storage->setValue(3,23, Value( 3 ) );
// storage->setValue(3,24, Value( 4 ) );
// storage->setValue(3,25, Value( 4 ) );
// storage->setValue(3,26, Value( 0 ) );
// storage->setValue(3,27, Value( 8 ) );
// storage->setValue(3,28, Value( 1 ) );
// storage->setValue(3,29, Value( 9 ) );
// storage->setValue(3,30, Value( 6 ) );
// storage->setValue(3,31, Value( 2 ) );
// // C51:C57
// storage->setValue(3,51, Value( 7 ) );
// storage->setValue(3,52, Value( 9 ) );
// storage->setValue(3,53, Value( 11 ) );
// storage->setValue(3,54, Value( 12 ) );
// storage->setValue(3,55, Value( 15 ) );
// storage->setValue(3,56, Value( 17 ) );
// storage->setValue(3,57, Value( 19 ) );
//
// // D51:D57
// storage->setValue(4,51, Value( 100 ) );
// storage->setValue(4,52, Value( 105 ) );
// storage->setValue(4,53, Value( 104 ) );
// storage->setValue(4,54, Value( 108 ) );
// storage->setValue(4,55, Value( 111 ) );
// storage->setValue(4,56, Value( 120 ) );
// storage->setValue(4,57, Value( 133 ) );
//
//
// // F51:F60
// storage->setValue(6,51, Value( 3 ) );
// storage->setValue(6,52, Value( 4 ) );
// storage->setValue(6,53, Value( 5 ) );
// storage->setValue(6,54, Value( 2 ) );
// storage->setValue(6,55, Value( 3 ) );
// storage->setValue(6,56, Value( 4 ) );
// storage->setValue(6,57, Value( 5 ) );
// storage->setValue(6,58, Value( 6 ) );
// storage->setValue(6,59, Value( 4 ) );
// storage->setValue(6,60, Value( 7 ) );
//
//
// // G51:G60
// storage->setValue(7,51, Value( 23 ) );
// storage->setValue(7,52, Value( 24 ) );
// storage->setValue(7,53, Value( 25 ) );
// storage->setValue(7,54, Value( 22 ) );
// storage->setValue(7,55, Value( 23 ) );
// storage->setValue(7,56, Value( 24 ) );
// storage->setValue(7,57, Value( 25 ) );
// storage->setValue(7,58, Value( 26 ) );
// storage->setValue(7,59, Value( 24 ) );
// storage->setValue(7,60, Value( 27 ) );
// A1000:G1000
storage->setValue(1, 1000, Value("abc"));
storage->setValue(2, 1000, Value("def"));
storage->setValue(3, 1000, Value("efoob"));
storage->setValue(4, 1000, Value("flka"));
storage->setValue(5, 1000, Value("kde"));
storage->setValue(6, 1000, Value("kde"));
storage->setValue(7, 1000, Value("xxx"));
// Z19:Z23
storage->setValue(26,19, Value( 16 ) );
storage->setValue(26,20, Value( 8 ) );
storage->setValue(26,21, Value( 4 ) );
storage->setValue(26,22, Value( 2 ) );
storage->setValue(26,23, Value( 1 ) );
// Add the second sheet
m_map->addNewSheet();
sheet = m_map->sheet(1);
sheet->setSheetName("Sheet2");
storage = sheet->cellStorage();
// B1:B2
Formula formula2(sheet);
formula2.setExpression("=SUM(Sheet1!A19:Sheet1!A31)");
storage->setFormula(2,1, formula2);
storage->setFormula(2,2, Formula::empty());
// Add the third sheet
m_map->addNewSheet();
sheet = m_map->sheet(2);
sheet->setSheetName("Sheet3");
storage = sheet->cellStorage();
// A1:A2
storage->setValue(1,1, Value( 1.1 ) );
storage->setValue(1,2, Value( 2.2 ) );
}
//
// unittests
//
void TestInformationFunctions::testAREAS()
{
CHECK_EVAL("AREAS(B3)", Value(1)); // A reference to a single cell has one area
CHECK_EVAL("AREAS(B3:C4)", Value(1)); // A reference to a single range has one area
// concatenation is not supported yet
// CHECK_EVAL( "AREAS(B3:C4~D5:D6)", Value( 2 ) ); // Cell concatenation creates multiple areas
// CHECK_EVAL( "AREAS(B3:C4~B3)", Value( 2 ) ); // Cell concatenation counts, even if the cells are duplicated
}
void TestInformationFunctions::testCELL()
{
CHECK_EVAL( "CELL(\"COL\";C7)", Value( 3 ) ); // Column C is column number 3.
CHECK_EVAL( "CELL(\"COL\";Sheet2!C7)", Value( 3 ) );
CHECK_EVAL( "CELL(\"ROW\";C7)", Value( 7 ) ); // Row 7 is row number 7.
CHECK_EVAL( "CELL(\"ROW\";Sheet2!C7)", Value( 7 ) );
CHECK_EVAL( "CELL(\"Sheet\";C7)", Value( 1 ) );
CHECK_EVAL( "CELL(\"Sheet\";Sheet2!C7)", Value( 2 ) );
CHECK_EVAL( "CELL(\"Sheet\";Sheet3!C7)", Value( 3 ) );
CHECK_EVAL( "CELL(\"ADDRESS\";B7)", Value( "$B$7" ) );
CHECK_EVAL( "CELL(\"ADDRESS\";Sheet2!B7)", Value( "'Sheet2'!$B$7" ) );
Value v1( "$B$7" );
Value r1 = evaluate("CELL(\"ADDRESS\")", v1, Cell(m_map->sheet(0), 2, 7));
QCOMPARE(r1, v1);
Value v2( "$B$7" );
Value r2 = evaluate("CELL(\"ADDRESS\")", v2, Cell(m_map->sheet(1), 2, 7));
QCOMPARE(r2, v2);
//CHECK_EVAL( "CELL(\"ADDRESS\";'x:\\sample.ods'#Sheet3!B7)", Value( "'file:///x:/sample.ods'#$Sheet3.$B$7" ) );
m_map->calculationSettings()->setFileName("/home/sample.ods");
CHECK_EVAL( "CELL(\"FILENAME\")", Value( "/home/sample.ods" ) );
CHECK_EVAL( "CELL(\"FILENAME\";B7)", Value( "/home/sample.ods" ) );
}
void TestInformationFunctions::testCOLUMN()
{
CHECK_EVAL("COLUMN(B7)", Value(2)); // Column "B" is column number 2.
// CHECK_EVAL( "COLUMN()", Value( 5 ) ); // Column of current cell is default, here formula in column E.
Value res(Value::Array);
res.setElement(0, 0, Value(2));
res.setElement(1, 0, Value(3));
res.setElement(2, 0, Value(4));
CHECK_EVAL("COLUMN(B2:D2)", res); // Array with column numbers.
}
void TestInformationFunctions::testCOLUMNS()
{
CHECK_EVAL("COLUMNS(C1)", Value(1)); // Single cell range contains one column.
CHECK_EVAL("COLUMNS(C1:C4)", Value(1)); // Range with only one column.
CHECK_EVAL("COLUMNS(A4:D100)", Value(4)); // Number of columns in range.
}
void TestInformationFunctions::testCOUNT()
{
CHECK_EVAL("COUNT(1;2;3)", Value(3)); // Simple count.
CHECK_EVAL("COUNT(B4:B5)", Value(2)); // Two numbers in the range.
CHECK_EVAL("COUNT(B4:B5;B4:B5)", Value(4)); // Duplicates are not removed.
CHECK_EVAL("COUNT(B4:B9)", Value(2)); // Errors in referenced cells or ranges are ignored.
CHECK_EVAL("COUNT(B4:B8;1/0)", Value(2)); // Errors in direct parameters are still ignored..
CHECK_EVAL("COUNT(B3:B5)", Value(2)); // Conversion to NumberSequence ignores strings (in B3).
}
void TestInformationFunctions::testCOUNTA()
{
CHECK_EVAL("COUNTA(\"1\";2;TRUE())", Value(3)); // Simple count of 3 constant values.
CHECK_EVAL("COUNTA(B3:B5)", Value(3)); // Three non-empty cells in the range.
CHECK_EVAL("COUNTA(B3:B5;B3:B5)", Value(6)); // Duplicates are not removed.
CHECK_EVAL("COUNTA(B3:B9)", Value(6)); // Where B9 is "=1/0", i.e. an error,
// counts the error as non-empty; errors contained
// in a reference do not propagate the error into the result.
CHECK_EVAL("COUNTA(\"1\";2;SUM(B3:B9))", Value(3)); // Errors in an evaluated formula do not propagate; they are just counted.
CHECK_EVAL("COUNTA(\"1\";2;B3:B9)", Value(8)); // Errors in the range do not propagate either
}
void TestInformationFunctions::testCOUNTBLANK()
{
CHECK_EVAL("COUNTBLANK(B3:B10)", Value(1)); // Only B8 is blank. Zero ('0') in B10 is not considered blank.
}
void TestInformationFunctions::testCOUNTIF()
{
CHECK_EVAL("COUNTIF(B4:B5;\">2.5\")", Value(1)); // B4 is 2 and B5 is 3, so there is one cell in the range with a value greater than 2.5.
CHECK_EVAL("COUNTIF(B3:B5;B4)", Value(1)); // Test if a cell equals the value in [.B4].
CHECK_EVAL("COUNTIF(\"\";B4)", Value::errorNA()); // Constant values are not allowed for the range.
CHECK_EVAL("COUNTIF(B3:B10;\"7\")", Value(1)); // [.B3] is the string "7".
CHECK_EVAL("COUNTIF(B3:B10;1+1)", Value(1)); // The criteria can be an expression.
}
void TestInformationFunctions::testERRORTYPE()
{
CHECK_EVAL("ERRORTYPE(0)", Value::errorVALUE()); // Non-errors produce an error.
CHECK_EVAL("ERRORTYPE(NA())", Value(7)); // By convention, the ERROR.TYPE of NA() is 7.
CHECK_EVAL("ERRORTYPE(1/0)", Value(2));
}
void TestInformationFunctions::testFORMULA()
{
CHECK_EVAL( "FORMULA(B1)", Value( "=SUM(A19:A31)" ) ); // B1 contains a simple SUM formula
CHECK_EVAL( "FORMULA(B2)", Value::errorNA() ); // Empty formula means no formula
CHECK_EVAL( "FORMULA(B3)", Value::errorNA() ); // Cell constants are not formulas
CHECK_EVAL( "LEN(FORMULA(B1))>0", Value( true ) ); // B7 is a formula, so this is fine and will produce a text value
}
void TestInformationFunctions::testINFO()
{
CHECK_EVAL("INFO(\"recalc\")", Value("Automatic")); //
CHECK_EVAL("ISTEXT(INFO(\"system\"))", Value(true)); // The details of "system" vary by system, but it is always a text value
CHECK_EVAL("ISTEXT(INFO(\"directory\"))", Value(true)); // Test to see that every required category is supported
// CHECK_EVAL( "ISNUMBER(INFO(\"memavail\"))", Value( true ) ); // not implemented
// CHECK_EVAL( "ISNUMBER(INFO(\"memused\"))", Value( true ) ); // not implemented
CHECK_EVAL("ISNUMBER(INFO(\"numfile\"))", Value(true)); //
CHECK_EVAL("ISTEXT(INFO(\"osversion\"))", Value(true)); //
// CHECK_EVAL( "ISTEXT(INFO(\"origin\"))", Value( true ) ); // not implemented
CHECK_EVAL("ISTEXT(INFO(\"recalc\"))", Value(true)); //
CHECK_EVAL("ISTEXT(INFO(\"release\"))", Value(true)); //
// CHECK_EVAL( "ISNUMBER(INFO(\"totmem\"))", Value( true ) ); // not implemented
// TODO should ISTEXT return errorVALUE() if args is errorVALUE? false seems to be more logical
// CHECK_EVAL( "ISTEXT(INFO(\"completely-unknown-category\"))", Value::errorVALUE() ); // Error if the category is unknown
}
void TestInformationFunctions::testISBLANK()
{
CHECK_EVAL("ISBLANK(1)", Value(false)); // Numbers return false.
CHECK_EVAL("ISBLANK(\"\")", Value(false)); // Text, even empty string, returns false.
CHECK_EVAL("ISBLANK(B8)", Value(true)); // Blank cell is true.
CHECK_EVAL("ISBLANK(B7)", Value(false)); // Non-blank cell is false.
}
void TestInformationFunctions::testISERR()
{
CHECK_EVAL("ISERR(1/0)", Value(true)); // Error values other than NA() return true.
CHECK_EVAL("ISERR(NA())", Value(false)); // NA() does NOT return True.
CHECK_EVAL("ISERR(\"#N/A\")", Value(false)); // Text is not an error.
CHECK_EVAL("ISERR(1)", Value(false)); // Numbers are not an error.
}
void TestInformationFunctions::testISERROR()
{
CHECK_EVAL("ISERROR(1/0)", Value(true)); // Error values return true.
CHECK_EVAL("ISERROR(NA())", Value(true)); // Even NA().
CHECK_EVAL("ISERROR(\"#N/A\")", Value(false)); // Text is not an error.
CHECK_EVAL("ISERROR(1)", Value(false)); // Numbers are not an error.
CHECK_EVAL("ISERROR(CHOOSE(0; \"Apple\"; \"Orange\";"
" \"Grape\"; \"Perry\"))", Value(true)); // If CHOOSE given
// out-of-range value, ISERROR needs to capture it.
}
void TestInformationFunctions::testISEVEN()
{
CHECK_EVAL("ISEVEN( 2)", Value(true)); // 2 is even, because (2 modulo 2) = 0
CHECK_EVAL("ISEVEN( 6)", Value(true)); // 6 is even, because (6 modulo 2) = 0
CHECK_EVAL("ISEVEN( 2.1)", Value(true)); //
CHECK_EVAL("ISEVEN( 2.5)", Value(true)); //
CHECK_EVAL("ISEVEN( 2.9)", Value(true)); // TRUNC(2.9)=2, and 2 is even.
CHECK_EVAL("ISEVEN( 3)", Value(false)); // 3 is not even.
CHECK_EVAL("ISEVEN( 3.9)", Value(false)); // TRUNC(3.9)=3, and 3 is not even.
CHECK_EVAL("ISEVEN(-2)", Value(true)); //
CHECK_EVAL("ISEVEN(-2.1)", Value(true)); //
CHECK_EVAL("ISEVEN(-2.5)", Value(true)); //
CHECK_EVAL("ISEVEN(-2.9)", Value(true)); // TRUNC(-2.9)=-2, and -2 is even.
CHECK_EVAL("ISEVEN(-3)", Value(false)); //
CHECK_EVAL("ISEVEN(NA())", Value::errorNA()); //
CHECK_EVAL("ISEVEN( 0)", Value(true)); //
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISEVEN(2.5)", Value(true)); // alternate function name
}
void TestInformationFunctions::testISFORMULA()
{
CHECK_EVAL( "ISFORMULA(B1)", Value( true ) ); // B1 contains a simple SUM formula
CHECK_EVAL( "ISFORMULA(B2)", Value( false ) ); // Empty formula means no formula
CHECK_EVAL( "ISFORMULA(B3)", Value( false ) ); // Cell constants are not formulas
}
void TestInformationFunctions::testISLOGICAL()
{
CHECK_EVAL("ISLOGICAL(TRUE())", Value(true)); // Logical values return true.
CHECK_EVAL("ISLOGICAL(FALSE())", Value(true)); // Logical values return true.
CHECK_EVAL("ISLOGICAL(\"TRUE\")", Value(false)); // Text values are not logicals,
// even if they can be converted.
}
void TestInformationFunctions::testISNONTEXT()
{
CHECK_EVAL("ISNONTEXT(1)", Value(true)); // Numbers are not text
CHECK_EVAL("ISNONTEXT(TRUE())", Value(true)); // Logical values are not text.
CHECK_EVAL("ISNONTEXT(\"1\")", Value(false)); // TexText values are text, even
// if they can be converted into a number.
CHECK_EVAL("ISNONTEXT(B7)", Value(false)); // B7 is a cell with text
CHECK_EVAL("ISNONTEXT(B9)", Value(true)); // B9 is an error, thus not text
CHECK_EVAL("ISNONTEXT(B8)", Value(true)); // B8 is a blank cell, so this will return TRUE
}
void TestInformationFunctions::testISNA()
{
CHECK_EVAL("ISNA(1/0)", Value(false)); // Error values other than NA() return False - the error does not propagate.
CHECK_EVAL("ISNA(NA())", Value(true)); // By definition
// CHECK_EVAL( "ISNA(#N/A)", Value( true ) ); // By definition
CHECK_EVAL("ISNA(\"#N/A\")", Value(false)); // Text is not NA
CHECK_EVAL("ISNA(1)", Value(false)); // Numbers are not NA
}
void TestInformationFunctions::testISNUMBER()
{
CHECK_EVAL("ISNUMBER(1)", Value(true)); // Numbers are numbers
CHECK_EVAL("ISNUMBER(\"1\")", Value(false)); // Text values are not numbers,
// even if they can be converted into a number.
}
void TestInformationFunctions::testISODD()
{
CHECK_EVAL("ISODD(3)", Value(true)); // 3 is odd, because (3 modulo 2) = 0
CHECK_EVAL("ISODD(5)", Value(true)); // 5 is odd, because (5 modulo 2) = 0
CHECK_EVAL("ISODD(3.1)", Value(true)); // TRUNC(3.1)=3, and 3 is odd
CHECK_EVAL("ISODD(3.5)", Value(true)); //
CHECK_EVAL("ISODD(3.9)", Value(true)); // TRUNC(3.9)=3, and 3 is odd.
CHECK_EVAL("ISODD(4)", Value(false)); // 4 is not even.
CHECK_EVAL("ISODD(4.9)", Value(false)); // TRUNC(4.9)=4, and 3 is not even.
CHECK_EVAL("ISODD(-3)", Value(true)); //
CHECK_EVAL("ISODD(-3.1)", Value(true)); //
CHECK_EVAL("ISODD(-3.5)", Value(true)); //
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISODD(3.1)", Value(true)); // alternate function name
}
void TestInformationFunctions::testISTEXT()
{
CHECK_EVAL("ISTEXT(1)", Value(false)); // Numbers are not text
CHECK_EVAL("ISTEXT(\"1\")", Value(true)); // Text values are text,
// even if they can be converted into a number.
}
void TestInformationFunctions::testISREF()
{
CHECK_EVAL("ISREF(B3)", Value(true)); //
CHECK_EVAL("ISREF(B3:C4)", Value(true)); // The range operator produces references
CHECK_EVAL("ISREF(1)", Value(false)); // Numbers are not references
CHECK_EVAL("ISREF(\"A1\")", Value(false)); // Text is not a reference, even if it looks a little like one
CHECK_EVAL("ISREF(NA())", Value::errorNA()); // Errors propagate through this function
}
void TestInformationFunctions::testN()
{
CHECK_EVAL("N(6)", Value(6)); // N does not change numbers.
CHECK_EVAL("N(TRUE())", Value(1)); // Does convert logicals.
CHECK_EVAL("N(FALSE())", Value(0)); // Does convert logicals.
}
void TestInformationFunctions::testNA()
{
CHECK_EVAL("ISERROR(NA())", Value(true)); // NA is an error.
CHECK_EVAL("ISNA(NA())", Value(true)); // Obviously, if this doesn't work, NA() or ISNA() is broken.
CHECK_EVAL("ISNA(5+NA())", Value(true)); // NA propagates through various functions
// and operators, just like any other error type.
}
// TODO row not working here
void TestInformationFunctions::testROW()
{
CHECK_EVAL("ROW(B7)", Value(7)); // The second value of a cell reference is the row number.
// CHECK_EVAL( "ROW()", Value( 5 ) ); // Row of current cell is default, here formula in row 5.
Value res(Value::Array);
res.setElement(0, 0, Value(2));
res.setElement(0, 1, Value(3));
res.setElement(0, 2, Value(4));
CHECK_EVAL("ROW(B2:B4)", res); // Array with row numbers.
}
void TestInformationFunctions::testROWS()
{
CHECK_EVAL("ROWS(C1)", Value(1)); // Single cell range contains one row.
CHECK_EVAL("ROWS(C1:C4)", Value(4)); // Range with four rows.
CHECK_EVAL("ROWS(A4:D100)", Value(97)); // Number of rows in range.
}
void TestInformationFunctions::testSHEET()
{
CHECK_EVAL("SHEET(B7)", Value(1));
CHECK_EVAL("SHEET(Sheet2!B7)", Value(2));
CHECK_EVAL("SHEET(Sheet3!B7)", Value(3));
CHECK_EVAL("SHEET()", Value(1));
}
void TestInformationFunctions::testSHEETS()
{
CHECK_EVAL( "SHEETS(B7)", Value( 1 ) ); // If given, the sheet number of the reference is used.
CHECK_EVAL( "SHEETS(Sheet1!B7:C9)", Value( 1 ) );
CHECK_EVAL( "SHEETS(Sheet1!A7:Sheet1!C9)", Value( 1 ) );
//TODO this should not fail! :-(
//CHECK_EVAL( "SHEETS(Sheet1!B7:Sheet2!C9)", Value( 2 ) );
//CHECK_EVAL( "SHEETS(Sheet1!B7:Sheet3!C9)", Value( 2 ) );
//CHECK_EVAL( "SHEETS(Sheet1!A7:Sheet3!C9)", Value( 3 ) );
CHECK_EVAL( "SHEETS()", Value( 3 ) ); // Count all sheets
}
void TestInformationFunctions::testTYPE()
{
// Value's Type | Type return
// --------------+-------------
// Number | 1
// Text | 2
// Logical | 4
// Error | 16
// Array | 64
CHECK_EVAL("TYPE(1+2)", Value(1)); // Number has TYPE code of 1
CHECK_EVAL("TYPE(\"Hi\"&\"there\")", Value(2)); // Text has TYPE 2
CHECK_EVAL("TYPE(NA())", Value(16)); // Errors have TYPE 16.
}
void TestInformationFunctions::testVALUE()
{
CHECK_EVAL("VALUE(\"6\")", Value(6));
CHECK_EVAL("VALUE(\"1E5\")", Value(100000));
CHECK_EVAL("VALUE(\"200%\")", Value(2));
CHECK_EVAL("VALUE(\"1.5\")", Value(1.5));
// Check fractions
CHECK_EVAL("VALUE(\"7 1/4\")", Value(7.25));
CHECK_EVAL("VALUE(\"0 1/2\")", Value(0.5));
CHECK_EVAL("VALUE(\"0 7/2\")", Value(3.5));
CHECK_EVAL("VALUE(\"-7 1/5\")", Value(-7.2));
CHECK_EVAL("VALUE(\"-7 10/50\")", Value(-7.2));
CHECK_EVAL("VALUE(\"-7 10/500\")", Value(-7.02));
CHECK_EVAL("VALUE(\"-7 4/2\")", Value(-9));
CHECK_EVAL("VALUE(\"-7 40/20\")", Value(-9));
// Check times
CHECK_EVAL("VALUE(\"00:00\")", Value(0));
CHECK_EVAL("VALUE(\"00:00:00\")", Value(0));
CHECK_EVAL("VALUE(\"02:00\")-2/24", Value(0));
CHECK_EVAL("VALUE(\"02:00:00\")-2/24", Value(0));
CHECK_EVAL("VALUE(\"02:00:00.0\")-2/24", Value(0));
CHECK_EVAL("VALUE(\"02:00:00.00\")-2/24", Value(0));
CHECK_EVAL("VALUE(\"02:00:00.000\")-2/24", Value(0));
CHECK_EVAL("VALUE(\"2:03:05\") -2/24-3/(24*60) -5/(24*60*60)", Value(0));
CHECK_EVAL("VALUE(\"2:03\")-(2/24)-(3/(24*60))", Value(0));
// check dates - local dependent
// CHECK_EVAL( "VALUE(\"5/21/06\")=DATE(2006;5;21)", Value( true ) );
// CHECK_EVAL( "VALUE(\"1/2/2005\")=DATE(2005;1;2)", Value( true ) );
}
void TestInformationFunctions::testMATCH()
{
// invalid matchType
CHECK_EVAL("MATCH(1;A19:A31;\"foo\")", Value::errorVALUE());
// matchType == 0, exact match
CHECK_EVAL("MATCH(5;C11:C17;0)", Value(1));
CHECK_EVAL("MATCH(8;C11:C17;0)", Value(3));
CHECK_EVAL("MATCH(1;C11:C17;0)", Value(7));
CHECK_EVAL("MATCH(13;C11:C17;0)", Value::errorNA());
CHECK_EVAL("MATCH(5;C11:C11;0)", Value(1));
CHECK_EVAL("MATCH(5;C11;0)", Value(1));
CHECK_EVAL("MATCH(5;C11:D13;0)", Value::errorNA()); // not sure if this is the best error
CHECK_EVAL("MATCH(\"Hello\";B3:B10;0)", Value(5));
CHECK_EVAL("MATCH(\"hello\";B3:B10;0)", Value(5)); // match is always case insensitive
CHECK_EVAL("MATCH(\"kde\";A1000:G1000;0)", Value(5));
// matchType == 1 or omitted, largest value less than or equal to search value in sorted range
CHECK_EVAL("MATCH(0;A19:A31;1)", Value::errorNA());
CHECK_EVAL("MATCH(1;A19:A31;1)", Value(1));
CHECK_EVAL("MATCH(16;A19:A31;1)", Value(5));
CHECK_EVAL("MATCH(40;A19:A31;1)", Value(6));
CHECK_EVAL("MATCH(4096;A19:A31;1)", Value(13));
CHECK_EVAL("MATCH(5000;A19:A31;1)", Value(13));
CHECK_EVAL("MATCH(\"aaa\";A1000:G1000)", Value::errorNA());
CHECK_EVAL("MATCH(\"abc\";A1000:G1000)", Value(1));
CHECK_EVAL("MATCH(\"efoob\";A1000:G1000)", Value(3));
CHECK_EVAL("MATCH(\"epub\";A1000:G1000)", Value(3));
CHECK_EVAL("MATCH(\"kde\";A1000:G1000)", Value(6));
CHECK_EVAL("MATCH(\"xxx\";A1000:G1000)", Value(7));
CHECK_EVAL("MATCH(\"zzz\";A1000:G1000)", Value(7));
CHECK_EVAL("MATCH(13;C11:D13;1)", Value::errorNA()); // not sure if this is the best error
// matchType == -1, smallest value greater than or equal to search value, in descending range
CHECK_EVAL("MATCH(0;Z19:Z23;-1)", Value(5));
CHECK_EVAL("MATCH(1;Z19:Z23;-1)", Value(5));
CHECK_EVAL("MATCH(4;Z19:Z23;-1)", Value(3));
CHECK_EVAL("MATCH(5;Z19:Z23;-1)", Value(2));
CHECK_EVAL("MATCH(16;Z19:Z23;-1)", Value(1));
CHECK_EVAL("MATCH(33;Z19:Z23;-1)", Value::errorNA());
CHECK_EVAL("MATCH(13;C11:D13;-1)", Value::errorNA()); // not sure if this is the best error
}
//
// cleanup test
//
void TestInformationFunctions::cleanupTestCase()
{
delete m_map;
}
-QTEST_KDEMAIN(TestInformationFunctions, GUI)
+QTEST_MAIN(TestInformationFunctions)
diff --git a/sheets/tests/TestInformationFunctions.h b/sheets/tests/TestInformationFunctions.h
index 26859c6b39b..a3cd124e729 100644
--- a/sheets/tests/TestInformationFunctions.h
+++ b/sheets/tests/TestInformationFunctions.h
@@ -1,89 +1,88 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_INFORMATION_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_INFORMATION_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
#include <Cell.h>
namespace Calligra
{
namespace Sheets
{
class Map;
class Cell;
class TestInformationFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testAREAS();
void testCELL();
void testCOLUMN();
void testCOLUMNS();
void testCOUNT();
void testCOUNTA();
void testCOUNTBLANK();
void testCOUNTIF();
void testERRORTYPE();
void testFORMULA();
void testINFO();
void testISBLANK();
void testISERR();
void testISERROR();
void testISEVEN();
void testISFORMULA();
void testISLOGICAL();
void testISNONTEXT();
void testISNA();
void testISNUMBER();
void testISODD();
void testISTEXT();
void testISREF();
void testMATCH();
void testN();
void testNA();
void testROW();
void testROWS();
void testSHEET();
void testSHEETS();
void testTYPE();
void testVALUE();
void cleanupTestCase();
private:
Value evaluate(const QString&, Value& ex, const Cell &cell = Cell());
Map* m_map;
};
} // namespace Sheets
} // namespace Calligra
#endif
diff --git a/sheets/tests/TestKspreadCommon.h b/sheets/tests/TestKspreadCommon.h
index 00d452bb12b..0b3a3cd0c2e 100644
--- a/sheets/tests/TestKspreadCommon.h
+++ b/sheets/tests/TestKspreadCommon.h
@@ -1,44 +1,44 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
-#include "qtest_kde.h"
-
#include <Formula.h>
#include <FunctionModuleRegistry.h>
#include <Value.h>
+#include <QTest>
+
#include <float.h> // DBL_EPSILON
#include <math.h>
using namespace Calligra::Sheets;
namespace QTest
{
template<>
char *toString(const Value& value)
{
QString message;
QTextStream ts(&message, QIODevice::WriteOnly);
ts << value;
return qstrdup(message.toLatin1());
}
}
diff --git a/sheets/tests/TestLogicFunctions.cpp b/sheets/tests/TestLogicFunctions.cpp
index 14a3e37200b..334592e9a5d 100644
--- a/sheets/tests/TestLogicFunctions.cpp
+++ b/sheets/tests/TestLogicFunctions.cpp
@@ -1,228 +1,228 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestLogicFunctions.h"
#include "TestKspreadCommon.h"
#include <KoPart.h>
#include <part/Doc.h>
#include <Map.h>
#include <Sheet.h>
using namespace Calligra::Sheets;
void TestLogicFunctions::init()
{
m_doc = new Doc(new MockPart);
m_doc->map()->addNewSheet();
m_sheet = m_doc->map()->sheet(0);
}
void TestLogicFunctions::cleanup()
{
delete m_doc;
}
void TestLogicFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x,y) { Value z = (y); QCOMPARE(evaluate(x,z),(z)); }
Value TestLogicFunctions::evaluate(const QString& formula, Value& ex)
{
Formula f(m_sheet);
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
void TestLogicFunctions::testAND()
{
CHECK_EVAL("AND(FALSE();FALSE())", Value(false));
CHECK_EVAL("AND(FALSE();TRUE())", Value(false));
CHECK_EVAL("AND(TRUE();FALSE())", Value(false));
CHECK_EVAL("AND(TRUE();TRUE())", Value(true));
// errors propagate
CHECK_EVAL("AND(TRUE();NA())", Value::errorNA());
CHECK_EVAL("AND(NA();TRUE())", Value::errorNA());
// Nonzero considered TRUE
CHECK_EVAL("AND(1;TRUE())", Value(true));
CHECK_EVAL("AND(2;TRUE())", Value(true));
// zero considered false
CHECK_EVAL("AND(0;TRUE())", Value(false));
// multiple parameters...
CHECK_EVAL("AND(TRUE();TRUE();TRUE())", Value(true));
CHECK_EVAL("AND(TRUE();TRUE();FALSE())", Value(false));
CHECK_EVAL("AND(FALSE();TRUE();TRUE())", Value(false));
CHECK_EVAL("AND(TRUE();FALSE();TRUE())", Value(false));
CHECK_EVAL("AND(TRUE();FALSE();FALSE())", Value(false));
// single parameter
CHECK_EVAL("AND(TRUE())", Value(true));
CHECK_EVAL("AND(FALSE())", Value(false));
// literal non-convertable text should give an error
//CHECK_EVAL("AND(FALSE();\"a\")", Value::errorVALUE());
}
void TestLogicFunctions::testFALSE()
{
CHECK_EVAL("FALSE()", Value(false));
// Applications that implement logical values as 0/1 must map FALSE() to 0
CHECK_EVAL("IF(ISNUMBER(FALSE());FALSE()=0;FALSE())", Value(false));
// note that kspread distinguishes between boolean and math
CHECK_EVAL("FALSE()=0", Value(false));
CHECK_EVAL("FALSE()=1", Value(false));
// False converts to 0 in Number context
CHECK_EVAL("2+FALSE()", Value(2));
}
void TestLogicFunctions::testIF()
{
CHECK_EVAL("IF(FALSE();7;8)", Value(8));
CHECK_EVAL("IF(TRUE();7;8)", Value(7));
CHECK_EVAL("IF(FALSE();7.1;8.2)", Value(8.2));
CHECK_EVAL("IF(TRUE();7.1;8.2)", Value(7.1));
CHECK_EVAL("IF(TRUE();\"HI\";8)", Value("HI"));
CHECK_EVAL("IF(1;7;8)", Value(7));
CHECK_EVAL("IF(5;7;8)", Value(7));
CHECK_EVAL("IF(0;7;8)", Value(8));
// there are a couple of indirect references in the spec test
// vectors here. Sorry
CHECK_EVAL("IF(\"x\";7;8)", Value::errorVALUE());
CHECK_EVAL("IF(\"1\";7;8)", Value::errorVALUE());
CHECK_EVAL("IF(\"\";7;8)", Value::errorVALUE());
CHECK_EVAL("IF(FALSE();7)", Value(false));
CHECK_EVAL("IF(FALSE();7;)", Value(0));
// Assuming A1 is an empty cell, using it in the following
// context should be different from passing no argument at all
CHECK_EVAL("IF(FALSE();7;A1)", Value(Value::Empty));
CHECK_EVAL("IF(TRUE();4;1/0)", Value(4));
CHECK_EVAL("IF(FALSE();1/0;5)", Value(5));
// Empty vs *
CHECK_EVAL("IF(A1==2;2;4)", Value(4));
CHECK_EVAL("IF(A1==2.5;2;4)", Value(4));
CHECK_EVAL("IF(A1==TRUE();2;4)", Value(4));
CHECK_EVAL("IF(A1==\"BAD\";2;4)", Value(4));
// from an excel binary document. seems it produces an parse error
CHECK_EVAL("IF(#-1#0#<>\"\";\"x \"&#-1#0#&#-30#0#;\"\")", Value::errorPARSE());
}
void TestLogicFunctions::testNOT()
{
CHECK_EVAL("NOT(FALSE())", Value(true));
CHECK_EVAL("NOT(TRUE())", Value(false));
CHECK_EVAL("NOT(1/0)", Value::errorDIV0());
CHECK_EVAL("NOT(\"a\")", Value::errorVALUE());
}
void TestLogicFunctions::testOR()
{
CHECK_EVAL("OR(FALSE();FALSE())", Value(false));
CHECK_EVAL("OR(FALSE();TRUE())", Value(true));
CHECK_EVAL("OR(TRUE();FALSE())", Value(true));
CHECK_EVAL("OR(TRUE();TRUE())", Value(true));
// errors propagate
CHECK_EVAL("OR(TRUE();NA())", Value::errorNA());
CHECK_EVAL("OR(NA();TRUE())", Value::errorNA());
// Nonzero considered TRUE
CHECK_EVAL("OR(1;TRUE())", Value(true));
CHECK_EVAL("OR(2;TRUE())", Value(true));
// zero considered false
CHECK_EVAL("OR(0;TRUE())", Value(true));
CHECK_EVAL("OR(0;1)", Value(true));
CHECK_EVAL("OR(0;0)", Value(false));
// multiple parameters...
CHECK_EVAL("OR(TRUE();TRUE();TRUE())", Value(true));
CHECK_EVAL("OR(FALSE();FALSE();FALSE())", Value(false));
CHECK_EVAL("OR(TRUE();TRUE();FALSE())", Value(true));
CHECK_EVAL("OR(FALSE();TRUE();TRUE())", Value(true));
CHECK_EVAL("OR(TRUE();FALSE();TRUE())", Value(true));
CHECK_EVAL("OR(TRUE();FALSE();FALSE())", Value(true));
// single parameter
CHECK_EVAL("OR(TRUE())", Value(true));
CHECK_EVAL("OR(FALSE())", Value(false));
// literal non-convertable text should give an error
//CHECK_EVAL("OR(TRUE();\"a\")", Value::errorVALUE());
}
void TestLogicFunctions::testTRUE()
{
CHECK_EVAL("TRUE()", Value(true));
// Applications that implement logical values as 0/1 must map TRUE() to 1
CHECK_EVAL("IF(ISNUMBER(TRUE());TRUE()=0;TRUE())", Value(true));
// note that kspread distinguishes between boolean and math
CHECK_EVAL("TRUE()=1", Value(false));
CHECK_EVAL("TRUE()=0", Value(false));
// False converts to 0 in Number context
CHECK_EVAL("2+TRUE()", Value(3));
}
void TestLogicFunctions::testXOR()
{
CHECK_EVAL("XOR(FALSE();FALSE())", Value(false));
CHECK_EVAL("XOR(FALSE();TRUE())", Value(true));
CHECK_EVAL("XOR(TRUE();FALSE())", Value(true));
CHECK_EVAL("XOR(TRUE();TRUE())", Value(false));
// errors propagate
CHECK_EVAL("XOR(TRUE();NA())", Value::errorNA());
CHECK_EVAL("XOR(NA();TRUE())", Value::errorNA());
CHECK_EVAL("XOR(FALSE();NA())", Value::errorNA());
CHECK_EVAL("XOR(NA();FALSE())", Value::errorNA());
// Nonzero considered TRUE
CHECK_EVAL("XOR(1;TRUE())", Value(false));
CHECK_EVAL("XOR(3;4)", Value(false));
CHECK_EVAL("XOR(2;TRUE())", Value(false));
CHECK_EVAL("XOR(FALSE();1)", Value(true));
CHECK_EVAL("XOR(2;FALSE())", Value(true));
// zero considered false
CHECK_EVAL("XOR(0;TRUE())", Value(true));
CHECK_EVAL("XOR(0;1)", Value(true));
CHECK_EVAL("XOR(0;0)", Value(false));
// multiple parameters...
CHECK_EVAL("XOR(TRUE();TRUE();TRUE())", Value(true));
CHECK_EVAL("XOR(TRUE();FALSE();TRUE();TRUE();TRUE();TRUE())", Value(true));
CHECK_EVAL("XOR(FALSE();FALSE();FALSE())", Value(false));
CHECK_EVAL("XOR(TRUE();TRUE();FALSE())", Value(false));
CHECK_EVAL("XOR(FALSE();TRUE();TRUE())", Value(false));
CHECK_EVAL("XOR(TRUE();FALSE();TRUE())", Value(false));
CHECK_EVAL("XOR(TRUE();FALSE();FALSE())", Value(true));
CHECK_EVAL("XOR(FALSE();FALSE();TRUE())", Value(true));
CHECK_EVAL("XOR(FALSE();FALSE();TRUE();FALSE())", Value(true));
// single parameter
CHECK_EVAL("XOR(TRUE())", Value(true));
CHECK_EVAL("XOR(FALSE())", Value(false));
// literal non-convertable text should give an error
//CHECK_EVAL("XOR(TRUE();\"a\")", Value::errorVALUE());
}
-QTEST_KDEMAIN(TestLogicFunctions, GUI)
+QTEST_MAIN(TestLogicFunctions)
diff --git a/sheets/tests/TestLogicFunctions.h b/sheets/tests/TestLogicFunctions.h
index 7219654b4c8..538ce49e0c5 100644
--- a/sheets/tests/TestLogicFunctions.h
+++ b/sheets/tests/TestLogicFunctions.h
@@ -1,61 +1,60 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_LOGIC_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_LOGIC_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class Doc;
class Sheet;
class TestLogicFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();
void initTestCase();
void testAND();
void testFALSE();
void testIF();
void testNOT();
void testOR();
void testTRUE();
void testXOR();
private:
Doc* m_doc;
Sheet* m_sheet;
Value evaluate(const QString&, Value& ex);
};
} // namespace Sheets
} // namespace Calligra
#endif
diff --git a/sheets/tests/TestMathFunctions.cpp b/sheets/tests/TestMathFunctions.cpp
index 75db19ae4c8..52ce0ebb3c9 100644
--- a/sheets/tests/TestMathFunctions.cpp
+++ b/sheets/tests/TestMathFunctions.cpp
@@ -1,958 +1,958 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestMathFunctions.h"
#include "TestKspreadCommon.h"
#include <CellStorage.h>
#include <Formula.h>
#include <Map.h>
#include <Sheet.h>
#include <CalculationSettings.h>
// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
// - different algorithms among spreadsheet programs
// - precision limitation of floating-point number representation
// - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x,y) QCOMPARE(TestDouble(x,y,6),y)
#define CHECK_EVAL_SHORT(x,y) QCOMPARE(TestDouble(x,y,10),y)
#define ROUND(x) (roundf(1e10 * x) / 1e10)
Value TestMathFunctions::TestDouble(const QString& formula, const Value& v2, int accuracy)
{
double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));
Formula f(m_map->sheet(0));
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
bool res = fabs(v2.asFloat() - result.asFloat()) < epsilon;
if (!res)
kDebug(36002) << "check failed -->" << "Epsilon =" << epsilon << "" << (double) v2.asFloat() << " to" << (double)result.asFloat() << " diff =" << (double)(v2.asFloat() - result.asFloat());
/* else
kDebug(36002)<<"check -->" <<" diff =" << v2.asFloat()-result.asFloat();*/
if (res)
return v2;
else
return result;
}
// round to get at most 10-digits number
inline static Value RoundNumber(double f)
{
return Value(ROUND(f));
}
// round to get at most 10-digits number
inline static Value RoundNumber(const Value& v)
{
if (v.isNumber()) {
double d = numToDouble(v.asFloat());
if (fabs(d) < DBL_EPSILON)
d = 0.0;
return Value(ROUND(d));
} else
return v;
}
Value TestMathFunctions::evaluate(const QString& formula)
{
Formula f(m_map->sheet(0));
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
#if 0
// this magically generates the CHECKs
printf(" CHECK_EVAL( \"%s\", %15g) );\n", qPrintable(formula), result.asFloat());
#endif
return RoundNumber(result);
}
void TestMathFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
m_map = new Map(0 /* no Doc */);
m_map->addNewSheet("Sheet1");
m_map->addNewSheet("Sheet2");
Sheet* sheet1 = m_map->sheet(0);
CellStorage* storage1 = sheet1->cellStorage();
// Sheet1!B3:B7
storage1->setValue(2, 3, Value("7"));
storage1->setValue(2, 4, Value(2));
storage1->setValue(2, 5, Value(3));
storage1->setValue(2, 6, Value(true));
storage1->setValue(2, 7, Value("Hello"));
// Sheet1!B9
storage1->setValue(2, 9, Value::errorDIV0());
Sheet* sheet2 = m_map->sheet(1);
CellStorage* storage2 = sheet2->cellStorage();
// Sheet2!A1:B13
storage2->setValue(1, 1, Value("test"));
storage2->setValue(2, 1, Value(1));
storage2->setValue(1, 2, Value("test1"));
storage2->setValue(2, 2, Value(2));
storage2->setValue(1, 3, Value("test2"));
storage2->setValue(2, 3, Value(3));
storage2->setValue(1, 4, Value("test1*"));
storage2->setValue(2, 4, Value(4));
storage2->setValue(1, 5, Value("test1.*"));
storage2->setValue(2, 5, Value(5));
storage2->setValue(1, 6, Value("TeSt"));
storage2->setValue(2, 6, Value(6));
storage2->setValue(1, 7, Value("Test1"));
storage2->setValue(2, 7, Value(7));
storage2->setValue(1, 8, Value("Test2"));
storage2->setValue(2, 8, Value(8));
storage2->setValue(1, 9, Value(" test"));
storage2->setValue(2, 9, Value(9));
storage2->setValue(1, 10, Value(" test1"));
storage2->setValue(2, 10, Value(10));
storage2->setValue(1, 11, Value("*test"));
storage2->setValue(2, 11, Value(11));
storage2->setValue(1, 12, Value("*test test"));
storage2->setValue(2, 12, Value(12));
storage2->setValue(1, 13, Value("^test"));
storage2->setValue(2, 13, Value(13));
}
void TestMathFunctions::cleanupTestCase()
{
delete m_map;
}
///////////////////////////////////////////////////////////////////////////////////////////////
void TestMathFunctions::testABS()
{
CHECK_EVAL("ABS(0)", Value(0));
CHECK_EVAL("ABS(-1)", Value(1));
CHECK_EVAL("ABS(-2)", Value(2));
CHECK_EVAL("ABS(-3)", Value(3));
CHECK_EVAL("ABS(-4)", Value(4));
CHECK_EVAL("ABS(1)", Value(1));
CHECK_EVAL("ABS(2)", Value(2));
CHECK_EVAL("ABS(3)", Value(3));
CHECK_EVAL("ABS(4)", Value(4));
CHECK_EVAL("ABS(1/0)", Value::errorDIV0());
}
void TestMathFunctions::testACOS()
{
// ODF-tests
CHECK_EVAL("ACOS(SQRT(2)/2)*4/PI()", Value(1.0)); // arc cosine of SQRT(2)/2 is PI()/4 radians.
CHECK_EVAL("ACOS(TRUE())", Value(0.0)); // TRUE() is 1 if inline.
CHECK_EVAL("ACOS(-1.0)/PI()", Value(1.0)); // The result must be between 0.0 and PI().
CHECK_EVAL("ACOS(2.0)", Value::errorVALUE()); // The argument must be between -1.0 and 1.0.
// ACosinus needs to be a numeric value between >=-1.0 and <=1.0
CHECK_EVAL("ACOS()", Value::errorVALUE());
CHECK_EVAL("ACOS(-1.1)", Value::errorVALUE());
CHECK_EVAL("ACOS(1.1)", Value::errorVALUE());
CHECK_EVAL("ACOS(1.0)", Value(0));
CHECK_EVAL_SHORT("2-ACOS(-1.0)", Value(-1.14159265));
}
void TestMathFunctions::testACOSH()
{
// ODF-tests
CHECK_EVAL("ACOSH(1)", Value(0)); //
CHECK_EVAL("ACOSH(2)", Value(1.316957897)); //
}
void TestMathFunctions::testACOT()
{
// ODF-tests
CHECK_EVAL("ACOT(0)-PI()/2", Value(0)); //
}
void TestMathFunctions::testACOTH()
{
// ODF-tests
CHECK_EVAL("ACOTH(2)", Value(0.5493061443)); //
}
void TestMathFunctions::testASIN()
{
// ODF-tests
CHECK_EVAL("ASIN(SQRT(2)/2)*4/PI()", Value(1.0)); // arc sine of SQRT(2)/2 is PI()/4 radians.
CHECK_EVAL("ASIN(TRUE())*2/PI()", Value(1.0)); // TRUE() is 1 if inline.
CHECK_EVAL("ASIN(-1)*2/PI()", Value(-1.0)); // The result must be between -PI()/2 and PI()/2.
CHECK_EVAL("ASIN(2)", Value::errorVALUE()); // The argument must be between -1.0 and 1.0.
// ASinus needs to be a numeric value between >=-1.0 and <=1.0
CHECK_EVAL("ASIN(1.2)", Value::errorVALUE());
CHECK_EVAL("ASIN(-99)", Value::errorVALUE());
CHECK_EVAL_SHORT("1-ASIN(1)", Value(-0.57079633));
CHECK_EVAL_SHORT("1+ASIN(-1.0)", Value(-0.57079633));
}
void TestMathFunctions::testASINH()
{
// ODF-tests
CHECK_EVAL("ASINH(0)", Value(0)); //
CHECK_EVAL("ASINH(1)", Value(0.881373587)); //
}
void TestMathFunctions::testATAN()
{
// ODF-tests
CHECK_EVAL("ATAN(1)*4/PI()", Value(1)); // arc tangent of 1 is PI()/4 radians.
CHECK_EVAL_SHORT("ATAN(-1.0e16)", Value(-1.570796)); // TODO expand / Check if ATAN gives reasonably accurate results,
// and that slightly negative values as input produce numbers near -PI/2.
}
void TestMathFunctions::testATAN2()
{
// ODF-tests
CHECK_EVAL("ATAN2(1;1)*4/PI()", Value(1)); // arc tangent of 1.0/1.0 is PI()/4 radians.
CHECK_EVAL("ATAN2(1;-1)*4/PI()", Value(-1)); // Location of sign makes a difference.
CHECK_EVAL("ATAN2(-1;1)*4/PI()", Value(3)); // Location of sign makes a difference.
CHECK_EVAL("ATAN2(-1;-1)*4/PI()", Value(-3)); // Location of sign makes a difference.
CHECK_EVAL("SIGN(ATAN2(-1.0;0.001))", Value(1)); // If y is small, it's still important
CHECK_EVAL("SIGN(ATAN2(-1.0;-0.001))", Value(-1)); // If y is small, it's still important
CHECK_EVAL("ATAN2(-1.0;0)/PI()", Value(1)); // By definition ATAN2(-1,0) should give PI() rather than -PI().
}
void TestMathFunctions::testATANH()
{
// ODF-tests
CHECK_EVAL_SHORT("ATANH(0)", Value(0)); // TODO expand
CHECK_EVAL_SHORT("ATANH(0.5)", Value(0.549306144)); // TODO expand
}
void TestMathFunctions::testBESSELI()
{
// ODF-tests
CHECK_EVAL_SHORT("BESSELI(2;2)", Value(0.688948)); // TODO expand
CHECK_EVAL_SHORT("BESSELI(0.7;3)", Value(0.007367));
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELI(2;2)", Value(0.688948));
}
void TestMathFunctions::testBESSELJ()
{
// ODF-tests
CHECK_EVAL_SHORT("BESSELJ(1;0)", Value(0.765198)); // TODO expand
CHECK_EVAL_SHORT("BESSELJ(0.89;3)", Value(0.013974));
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELJ(1;0)", Value(0.765198));
}
void TestMathFunctions::testBESSELK()
{
// ODF-tests
CHECK_EVAL_SHORT("BESSELK(3;0)", Value(0.03474)); // TODO expand
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELK(3;0)", Value(0.03474));
}
void TestMathFunctions::testBESSELY()
{
// ODF-tests
CHECK_EVAL_SHORT("BESSELY(1;1)", Value(-0.781213)); // TODO expand
CHECK_EVAL_SHORT("BESSELY(4;2)", Value(0.215903595));
// alternate function name
CHECK_EVAL_SHORT("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELY(1;1)", Value(-0.781213));
}
void TestMathFunctions::testCEIL()
{
CHECK_EVAL("CEIL(0)", Value(0));
CHECK_EVAL("CEIL(0.1)", Value(1));
CHECK_EVAL("CEIL(0.01)", Value(1));
CHECK_EVAL("CEIL(0.001)", Value(1));
CHECK_EVAL("CEIL(0.0001)", Value(1));
CHECK_EVAL("CEIL(0.00001)", Value(1));
CHECK_EVAL("CEIL(0.000001)", Value(1));
CHECK_EVAL("CEIL(0.0000001)", Value(1));
CHECK_EVAL("CEIL(1.1)", Value(2));
CHECK_EVAL("CEIL(1.01)", Value(2));
CHECK_EVAL("CEIL(1.001)", Value(2));
CHECK_EVAL("CEIL(1.0001)", Value(2));
CHECK_EVAL("CEIL(1.00001)", Value(2));
CHECK_EVAL("CEIL(1.000001)", Value(2));
CHECK_EVAL("CEIL(1.0000001)", Value(2));
CHECK_EVAL("CEIL(-0.1)", Value(0));
CHECK_EVAL("CEIL(-0.01)", Value(0));
CHECK_EVAL("CEIL(-0.001)", Value(0));
CHECK_EVAL("CEIL(-0.0001)", Value(0));
CHECK_EVAL("CEIL(-0.00001)", Value(0));
CHECK_EVAL("CEIL(-0.000001)", Value(0));
CHECK_EVAL("CEIL(-0.0000001)", Value(0));
CHECK_EVAL("CEIL(-1.1)", Value(-1));
CHECK_EVAL("CEIL(-1.01)", Value(-1));
CHECK_EVAL("CEIL(-1.001)", Value(-1));
CHECK_EVAL("CEIL(-1.0001)", Value(-1));
CHECK_EVAL("CEIL(-1.00001)", Value(-1));
CHECK_EVAL("CEIL(-1.000001)", Value(-1));
CHECK_EVAL("CEIL(-1.0000001)", Value(-1));
}
void TestMathFunctions::testCEILING()
{
CHECK_EVAL("CEILING(0 ; 0.1)", Value(0));
CHECK_EVAL("CEILING(0 ; 0.2)", Value(0));
CHECK_EVAL("CEILING(0 ; 1.0)", Value(0));
CHECK_EVAL("CEILING(0 ;10.0)", Value(0));
CHECK_EVAL("CEILING(0.1; 0.2)", Value(0.2));
CHECK_EVAL("CEILING(0.1; 0.4)", Value(0.4));
CHECK_EVAL("CEILING(1.1; 0.2)", Value(1.2));
// because can't divide by 0
CHECK_EVAL("CEILING(1; 0)", Value::errorDIV0());
CHECK_EVAL("CEILING(2; 0)", Value::errorDIV0());
// but this one should be just fine !
CHECK_EVAL("CEILING(0; 0)", Value(0));
// different sign does not make sense
CHECK_EVAL("CEILING(-1; 2)", Value::errorNUM());
CHECK_EVAL("CEILING(1; -2)", Value::errorNUM());
// mode param
CHECK_EVAL("CEILING(-5.4; -4.0; 0)", Value(-4.0));
CHECK_EVAL("CEILING(-5.4; -4.0; 1)", Value(-8.0));
}
void TestMathFunctions::testCOMBIN()
{
// ODF-tests
CHECK_EVAL("COMBIN(5;3)", Value(10)); //
CHECK_EVAL("COMBIN(6;3)", Value(20)); //
CHECK_EVAL("COMBIN(42;3)", Value(11480)); //
CHECK_EVAL("COMBIN(-1;3)", Value::errorNUM()); // N must be >= 0
CHECK_EVAL("COMBIN(4;-3)", Value::errorNUM()); // M must be >= 0
}
void TestMathFunctions::testCOMBINA()
{
// ODF-tests
CHECK_EVAL("COMBINA(5;3)", Value(35)); //
CHECK_EVAL("COMBINA(-1;3)", Value::errorNUM()); // N must be >= 0
CHECK_EVAL("COMBINA(4;-3)", Value::errorNUM()); // M must be >= 0
}
void TestMathFunctions::testCONVERT()
{
// ODF-tests
// TODO add missing SI-units and expand up to 10 digits
CHECK_EVAL("CONVERT( 1; \"ft\"; \"in\")", Value(12)); // 1 foot is 12 inches. Conversion between
// units might involve an intermediate SI unit
// in some implementations, and such round-off
// error is considered acceptable.
CHECK_EVAL("CONVERT( 1; \"in\"; \"cm\")", Value(2.54)); // 1 inch is 2.54 cm. The result is exact, because
// this needs to be represented as accurately as the
// underlying numerical model permits
CHECK_EVAL("CONVERT( 5; \"m\"; \"mm\")", Value(5000)); // 5 meters is 5000 millimeters
CHECK_EVAL("CONVERT( 100; \"C\"; \"F\")", Value(212)); // 212 degrees F is 100 degrees C. Note that this
// is not simply a multiplicative relationship.
// Since internally this is (100/5*9+32), where
// 100/5 is exactly 20, this result needs to be
// exact even on floating-point implementations
CHECK_EVAL("CONVERT( 2; \"Ym\"; \"Zm\")", Value(2000)); // Must support Y and Z prefixes. (wrong ODF-specs 100)
CHECK_EVAL("CONVERT( 20; \"F\"; \"m\")", Value::errorNA()); // Different groups produce an error.
CHECK_EVAL_SHORT("CONVERT(1000;\"qt\";\"l\")", Value(946.5588641)); // Quart is U.S. customary, liquid measure
CHECK_EVAL_SHORT("CONVERT(1000;\"tbs\";\"l\")", Value(14.78998225)); // Tablespoon uses U.S. customary historic definition
// - note that there are many other definitions
CHECK_EVAL("CONVERT(1000; \"tsp\"; \"l\")", Value(4.929994084)); // Teaspoon uses U.S. customary historic definition
// - note that there are many other definitions
CHECK_EVAL("CONVERT( 1; \"das\"; \"sec\")", Value(10)); // Does it support both "s" and "sec" for second?
// Does it support "da" as the SI standard deka prefix?
CHECK_EVAL("CONVERT( 1; \"ar\"; \"m^2\")", Value(100)); // A hectare (ar) is 100 square meters.
// CHECK_EVAL( "CONVERT( 1; \"cal\"; \"J\")", Value( 4.1868 ) ); // "cal" is an International Table (IT) calorie, 4.1868 J.
CHECK_EVAL("CONVERT( 1; \"lbf\"; \"N\")", Value(4.448222)); // Converting pound-force to Newtons
CHECK_EVAL("CONVERT( 1; \"HP\"; \"W\")", Value(745.701)); // Horsepower to Watts
CHECK_EVAL("CONVERT( 1; \"Mibyte\"; \"bit\")", Value(8388608)); // Converts bytes to bits, and tests binary prefixes
CHECK_EVAL("CONVERT( 1; \"Gibyte\"; \"Mibyte\")", Value(1024)); // Converts bytes to bits, and tests binary prefixes
CHECK_EVAL("CONVERT( 1; \"T\"; \"ga\")", Value(10000)); // Tesla to Gauss
// CHECK_EVAL( "CONVERT( 1; \"lbm\"; \"g\")", Value( 453.59237 ) ); // International pound mass (avoirdupois) to grams.
// (This is actually exact.)
CHECK_EVAL("CONVERT( 1; \"uk_ton\"; \"lbm\")", Value(2240)); // Imperial ton, aka "long ton", "deadweight ton",
// or "weight ton", is 2240 lbm.
// CHECK_EVAL( "CONVERT( 1; \"psi\"; \"Pa\")", Value( 6894.76 ) ); // Pounds per square inch to Pascals.
CHECK_EVAL("CONVERT( 60; \"mph\"; \"km/h\")", Value(96.56064)); // Miles per hour to kilometers per hour.
CHECK_EVAL("CONVERT( 1; \"day\"; \"s\")", Value(86400)); // Day to seconds. Note: This test uses the
// international standard abbreviation for second (s),
// not the abbreviation traditionally used in spreadsheets
// (sec); both "s" and "sec" must be supported.
// CHECK_EVAL_SHORT( "CONVERT( 1; \"qt\"; \"L\")", Value( 0.9463529460 ) ); // Quart (U.S. customary liquid measure) to liter.
// This is 0.946352946 liters,
}
void TestMathFunctions::testCOT()
{
// ODF-tests
CHECK_EVAL("COT(PI()/4.0)", Value(1)); // cotangent of PI()/4.0 radians.
CHECK_EVAL("COT(PI()/2.0)", Value(0)); // cotangent of PI()/2 radians. Not the same as TAN.
CHECK_EVAL("COT(0)", Value::errorDIV0()); // cotangent of PI()/4.0 radians.
}
void TestMathFunctions::testCOTH()
{
// ODF-tests
CHECK_EVAL("COTH(1)", Value(1.3130352855)); //
CHECK_EVAL("COTH(EXP(1))", Value(1.0087469296)); //
}
void TestMathFunctions::testDEGREES()
{
// ODF-tests
CHECK_EVAL("DEGREES(PI())", Value(180)); // PI() radians is 180 degrees.
}
void TestMathFunctions::testDELTA()
{
// ODF-tests
CHECK_EVAL("DELTA(2;3)", Value(0)); // Different numbers are not equal
CHECK_EVAL("DELTA(2;2)", Value(1)); // Same numbers are equal
CHECK_EVAL("DELTA(0)" , Value(1)); // 0 equal to default 0
}
void TestMathFunctions::testEVEN()
{
// ODF-tests
CHECK_EVAL("EVEN(6)", Value(6)); // Positive even integers remain unchanged.
CHECK_EVAL("EVEN(-4)", Value(-4)); // Negative even integers remain unchanged.
CHECK_EVAL("EVEN(1)", Value(2)); // Non-even positive integers round up.
CHECK_EVAL("EVEN(0.3)", Value(2)); // Positive floating values round up.
CHECK_EVAL("EVEN(-1)", Value(-2)); // Non-even negative integers round down.
CHECK_EVAL("EVEN(-0.3)", Value(-2)); // Negative floating values round down.
CHECK_EVAL("EVEN(0)", Value(0)); // Since zero is even, EVEN(0) returns zero.
}
void TestMathFunctions::testEXP()
{
// ODF-tests
CHECK_EVAL("EXP(0)", Value(1)); // Anything raised to the 0 power is 1.
CHECK_EVAL("EXP(LN(2))", Value(2)); // The EXP function is the inverse of the LN function.
CHECK_EVAL("EXP(1)", Value(2.71828182845904523536)); // The value of the natural logarithm e.
}
void TestMathFunctions::testFACT()
{
CHECK_EVAL("FACT(0)", Value(1));
CHECK_EVAL("FACT(1)", Value(1));
CHECK_EVAL("FACT(2)", Value(2));
CHECK_EVAL("FACT(3)", Value(6));
CHECK_EVAL("FACT(-1)", Value::errorNUM());
CHECK_EVAL("FACT(\"xyzzy\")", Value::errorNUM());
}
void TestMathFunctions::testFACTDOUBLE()
{
CHECK_EVAL("FACTDOUBLE(0)", Value(1));
CHECK_EVAL("FACTDOUBLE(1)", Value(1));
CHECK_EVAL("FACTDOUBLE(7)", Value(105));
CHECK_EVAL("FACTDOUBLE(6)", Value(48));
CHECK_EVAL("FACTDOUBLE(-1)", Value::errorNUM());
CHECK_EVAL("FACTDOUBLE(\"xyzzy\")", Value::errorNUM());
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFACTDOUBLE(7)", Value(105)); // alternate function name
}
void TestMathFunctions::testFIB()
{
CHECK_EVAL("FIB(1)", Value(1));
CHECK_EVAL("FIB(2)", Value(1));
CHECK_EVAL("FIB(3)", Value(2));
CHECK_EVAL("FIB(4)", Value(3));
CHECK_EVAL("FIB(5)", Value(5));
CHECK_EVAL("FIB(6)", Value(8));
CHECK_EVAL("FIB(7)", Value(13));
CHECK_EVAL("FIB(8)", Value(21));
CHECK_EVAL("FIB(9)", Value(34));
CHECK_EVAL("FIB(10)", Value(55));
// large number
CHECK_EVAL("FIB(100)/1E+20", Value(3.54224848179263));
CHECK_EVAL("FIB(200)/1E+41", Value(2.80571172992512));
CHECK_EVAL("FIB(300)/1E+62", Value(2.22232244629423));
CHECK_EVAL("FIB(400)/1E+83", Value(1.76023680645016));
CHECK_EVAL("FIB(500)/1E+104", Value(1.394232245617));
CHECK_EVAL("FIB(600)/1E+125", Value(1.10433070572954));
// invalid
CHECK_EVAL("FIB(0)", Value::errorNUM());
CHECK_EVAL("FIB(-1)", Value::errorNUM());
CHECK_EVAL("FIB(\"text\")", Value::errorVALUE());
}
void TestMathFunctions::testFLOOR()
{
// ODF-Tests
CHECK_EVAL("=FLOOR(2; 1)", Value(2));
CHECK_EVAL("=FLOOR(2.5; 1)", Value(2));
CHECK_EVAL("=FLOOR(5; 2)", Value(4));
CHECK_EVAL("=FLOOR(5; 2.2)", Value(4.4));
CHECK_EVAL("=FLOOR(-2.5;1)", Value::errorVALUE());
CHECK_EVAL("=FLOOR(-2.5; -1)", Value(-3));
CHECK_EVAL("=FLOOR(-2.5; -1;1)", Value(-2));
CHECK_EVAL("=FLOOR(-2.5;0)", Value(0));
CHECK_EVAL("=FLOOR(0;-1)", Value(0));
CHECK_EVAL("=FLOOR(-1.1)", Value(-2));
}
void TestMathFunctions::testGAMMA()
{
// ODF-Tests
CHECK_EVAL("GAMMA(1.00)", Value(1.0000000000));
CHECK_EVAL("GAMMA(1.10)", Value(0.9513507700));
CHECK_EVAL("GAMMA(1.50)", Value(0.8862269255));
}
void TestMathFunctions::testGAMMALN()
{
// ODF-Tests
CHECK_EVAL("GAMMALN(1.00)", Value(0));
CHECK_EVAL("GAMMALN(2.00)", Value(0));
CHECK_EVAL("GAMMALN(3.00)", Value(0.6931471806));
CHECK_EVAL("GAMMALN(1.50)", Value(-0.1207822376));
}
void TestMathFunctions::testGCD()
{
CHECK_EVAL("GCD(5;15;25)", Value(5));
CHECK_EVAL("GCD(2;3)", Value(1));
CHECK_EVAL("GCD(18;24)", Value(6));
CHECK_EVAL("GCD(18.1;24.1)", Value(6));
CHECK_EVAL("GCD(1.1;2.2)", Value(1));
CHECK_EVAL("GCD(18.9;24.9)", Value(6));
CHECK_EVAL("GCD(7)", Value(7));
CHECK_EVAL("GCD(5;0)", Value(5));
CHECK_EVAL("GCD(0;0)", Value(0));
CHECK_EVAL("GCD(-2;3)", Value::errorNUM());
CHECK_EVAL("GCD(2;-4)", Value::errorNUM());
CHECK_EVAL("GCD(-2;-4)", Value::errorNUM());
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGCD(18;24)", Value(6)); // alternate function name
}
void TestMathFunctions::testGESTEP()
{
// ODF-tests
CHECK_EVAL("GESTEP(2;1)", Value(1)); //
CHECK_EVAL("GESTEP(-1;-2)", Value(1)); // Negative arguments are valid
CHECK_EVAL("GESTEP(1)", Value(1)); // Second parameter assumed 0 if omitted
CHECK_EVAL("GESTEP(-2;1)", Value(0)); //
CHECK_EVAL("GESTEP(3;3)", Value(1)); // Number identical to step value.
CHECK_EVAL("GESTEP(1.3;1.2)", Value(1)); // Floating point values where X is greater than Step.
CHECK_EVAL("GESTEP(-2;\"xxx\")", Value::errorNUM()); //
CHECK_EVAL("GESTEP(\"xxx\";-2)", Value::errorNUM()); //
}
void TestMathFunctions::testINT()
{
// ODF-tests
CHECK_EVAL("=INT(2)", Value(2));
CHECK_EVAL("=INT(-3)", Value(-3));
CHECK_EVAL("=INT(1.2)", Value(1));
CHECK_EVAL("=INT(1.7)", Value(1));
CHECK_EVAL("=INT(-1.2)", Value(-2));
CHECK_EVAL("=INT((1/3)*3)", Value(1));
}
void TestMathFunctions::testLCM()
{
CHECK_EVAL("LCM(5;15;25)", Value(75));
CHECK_EVAL("LCM(2;3)", Value(6));
CHECK_EVAL("LCM(18;12)", Value(36));
CHECK_EVAL("LCM(12;18)", Value(36));
CHECK_EVAL("LCM(12.1;18.1)", Value(36));
CHECK_EVAL("LCM(18.1;12.1)", Value(36));
CHECK_EVAL("LCM(18.9;12.9)", Value(36));
CHECK_EVAL("LCM(7)", Value(7));
CHECK_EVAL("LCM(5;0)", Value(0));
CHECK_EVAL("LCM(-2;4)", Value::errorNUM());
CHECK_EVAL("LCM(2;-4)", Value::errorNUM());
CHECK_EVAL("LCM(-2;-4)", Value::errorNUM());
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETLCM(18;12)", Value(36)); // alternate function name
}
void TestMathFunctions::testLN()
{
// ODF-tests
CHECK_EVAL("LN(1)", Value(0)); // The logarithm of 1 (in any base) is 0.
CHECK_EVAL("LN(EXP(1))", Value(1)); // The natural logarithm of e is 1.
CHECK_EVAL_SHORT("LN(20)", Value(2.995732274)); // TODO expand / Trivial test
CHECK_EVAL_SHORT("LN(0.2)", Value(-1.609437912)); // TODO expand / This tests a value between 0 and 0.5.
// Values in this domain are valid, but implementations that compute LN(x)
// by blindly summing the series (1/n)((x-1)/x)^n won't get this value
// correct, because that series requires x > 0.5.
CHECK_EVAL("LN(0)", Value::errorNUM()); // The argument must be greater than zero.
CHECK_EVAL("LN(\"s\")", Value::errorNUM()); // The argument must be a number.
}
void TestMathFunctions::testLOG()
{
CHECK_EVAL("LOG(1;10)", Value(0));
CHECK_EVAL("LOG(1;EXP(1))", Value(0));
CHECK_EVAL("LOG(10;10)", Value(1));
CHECK_EVAL("LOG(EXP(1);EXP(1))", Value(1));
CHECK_EVAL("LOG(10)", Value(1));
CHECK_EVAL("LOG(8*8*8;8)", Value(3));
CHECK_EVAL("LOG(0;10)", Value::errorNUM());
CHECK_EVAL("LOG(\"foo\";10)", Value::errorNUM());
CHECK_EVAL("LOG(2;\"foo\")", Value::errorNUM());
CHECK_EVAL("LOG(NA();10)", Value::errorNA());
CHECK_EVAL("LOG(10;NA())", Value::errorNA());
CHECK_EVAL("LOG(NA();NA())", Value::errorNA());
}
void TestMathFunctions::testLOG10()
{
CHECK_EVAL("LOG10(1)", Value(0));
CHECK_EVAL("LOG10(10)", Value(1));
CHECK_EVAL("LOG10(100)", Value(2));
CHECK_EVAL("LOG10(0)", Value::errorNUM());
CHECK_EVAL("LOG10(\"H\")", Value::errorNUM());
CHECK_EVAL("LOG10(-2)", Value::errorNUM());
}
void TestMathFunctions::testMDETERM()
{
CHECK_EVAL("MDETERM({2;4|3;5})", Value(-2));
CHECK_EVAL("MDETERM({2;4})", Value::errorVALUE());
CHECK_EVAL("MDETERM({2;4|3;6})", Value(0));
CHECK_EVAL("MDETERM(2)", Value(2));
}
void TestMathFunctions::testMINVERSE()
{
Value value(Value::Array);
value.setElement(0, 0, Value(-2.5));
value.setElement(1, 0, Value(2.0));
value.setElement(0, 1, Value(1.5));
value.setElement(1, 1, Value(-1.0));
CHECK_EVAL("MINVERSE({2;4|3;5})", value); // simply invertible
value.setElement(0, 0, Value(5.0));
value.setElement(1, 0, Value(1.0));
value.setElement(2, 0, Value(-2.0));
value.setElement(0, 1, Value(-1.0));
value.setElement(1, 1, Value(-1.0));
value.setElement(2, 1, Value(1.0));
value.setElement(0, 1, Value(-2.0));
value.setElement(1, 1, Value(1.0));
value.setElement(2, 1, Value(0.0));
CHECK_EVAL("MINVERSE({1;2;1|2;4;3|3;7;4}", value); // fails without pivoting
CHECK_EVAL("MINVERSE({2;4})", Value::errorVALUE()); // non-square matrix
CHECK_EVAL("MINVERSE({2;4|3;6})", Value::errorDIV0()); // singular matrix
CHECK_EVAL("MINVERSE(2)", evaluate("{0.5}")); // one elementary matrix
}
void TestMathFunctions::testMMULT()
{
CHECK_EVAL("MMULT({2;4|3;5};{2;4|3;5})", evaluate("{16.0;28.0|21.0;37.0}"));
}
void TestMathFunctions::testMOD()
{
CHECK_EVAL("MOD(10;3)", Value(1)); // 10/3 has remainder 1.
CHECK_EVAL("MOD(2;8)", Value(2)); // 2/8 is 0 remainder 2.
CHECK_EVAL("MOD(5.5;2.5)", Value(0.5)); // The numbers need not be integers.
CHECK_EVAL("MOD(-2;3)", Value(1)); // The location of the sign matters.
CHECK_EVAL("MOD(2;-3)", Value(-1)); // The location of the sign matters.
CHECK_EVAL("MOD(-2;-3)", Value(-2)); // The location of the sign matters.
CHECK_EVAL("MOD(10;3)", Value(1)); // 10/3 has remainder 1.
CHECK_EVAL("MOD(10;0)", Value::errorDIV0()); // Division by zero is not allowed
}
void TestMathFunctions::testMROUND()
{
// ODF-tests
CHECK_EVAL("=MROUND(1564;100)", Value(1600));
CHECK_EVAL("=MROUND(1520;100)", Value(1500));
CHECK_EVAL("=MROUND(1550;100)", Value(1600));
CHECK_EVAL("=MROUND(41.89;8)", Value(40));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMROUND(1520;100)", Value(1500));
}
void TestMathFunctions::testMULTINOMIAL()
{
// ODF-tests
CHECK_EVAL("=MULTINOMIAL(3;4;5)", Value(27720));
// alternate function name
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMULTINOMIAL(3;4;5)", Value(27720));
}
void TestMathFunctions::testMUNIT()
{
CHECK_EVAL("MUNIT(2)", evaluate("{1;0|0;1}"));
CHECK_EVAL("MUNIT(3)", evaluate("{1;0;0|0;1;0|0;0;1}"));
}
void TestMathFunctions::testODD()
{
CHECK_EVAL("ODD(5)", Value(5));
CHECK_EVAL("ODD(-5)", Value(-5));
CHECK_EVAL("ODD(2)", Value(3));
CHECK_EVAL("ODD(0.3)", Value(1));
CHECK_EVAL("ODD(-2)", Value(-3));
CHECK_EVAL("ODD(-0.3)", Value(-1));
CHECK_EVAL("ODD(0)", Value(1));
}
void TestMathFunctions::testPOWER()
{
CHECK_EVAL("POWER(10;0)", Value(1)); // Anything raised to the 0 power is 1
CHECK_EVAL("POWER(2;8)" , Value(256)); // 2^8 is 256
}
void TestMathFunctions::testPRODUCT()
{
CHECK_EVAL("PRODUCT(2;3;4)", Value(24)); // Anything raised to the 0 power is 1
CHECK_EVAL("PRODUCT(TRUE();2;3)" , Value(6)); // TRUE() is 1 if inline
CHECK_EVAL("PRODUCT()", Value(0)); // Product with no parameters returns 0
//TODO
// check inline-values e.g. product(2;3;"2")
}
void TestMathFunctions::testQUOTIENT()
{
CHECK_EVAL("QUOTIENT(10;5)", Value(2)); //
CHECK_EVAL("QUOTIENT(14;5)" , Value(2)); //
CHECK_EVAL("QUOTIENT(-204;-23)", Value(8)); //
CHECK_EVAL("QUOTIENT(-45;8)", Value(-5)); //
CHECK_EVAL("QUOTIENT(24;-5)" , Value(-4)); //
CHECK_EVAL("QUOTIENT(21;-5)", Value(-4)); //
CHECK_EVAL("QUOTIENT(-14;5)", Value(-2)); //
CHECK_EVAL("QUOTIENT(5;0)" , Value::errorDIV0()); //
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETQUOTIENT(14;5)", Value(2)); // alternate function name
}
void TestMathFunctions::testRADIANS()
{
CHECK_EVAL("RADIANS(180)/PI()", Value(1)); // 180 degrees is PI() radians.
}
void TestMathFunctions::testRAND()
{
CHECK_EVAL("RAND()>=0", Value(true)); // The random number must be between 0 and 1.
CHECK_EVAL("RAND()<=1", Value(true)); // The random number must be between 0 and 1.
}
void TestMathFunctions::testRANDBETWEEN()
{
CHECK_EVAL("RANDBETWEEN(8;8)", Value(8)); // If A=B, return A.
CHECK_EVAL("RANDBETWEEN(5;15)>=5", Value(true)); // Must return value in range
CHECK_EVAL("RANDBETWEEN(5;15)<=15", Value(true)); // Must return value in range
CHECK_EVAL("RANDBETWEEN(15;5)>=5", Value(true)); // Must return value in range
CHECK_EVAL("RANDBETWEEN(15;5)<=15", Value(true)); // Must return value in range
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRANDBETWEEN(8;8)", Value(8)); // alternate function name
}
void TestMathFunctions::testROUND()
{
// ODF-tests
CHECK_EVAL("=ROUND(10.1;0)", Value(10));
CHECK_EVAL("=ROUND(9.8;0)", Value(10));
CHECK_EVAL("=ROUND(0.5;0)", Value(1));
CHECK_EVAL("=ROUND(1/3;0) ", Value(0));
CHECK_EVAL("=ROUND(1/3;1)", Value(0.3));
CHECK_EVAL("=ROUND(1/3;2)", Value(0.33));
CHECK_EVAL("=ROUND(1/3;2.9)", Value(0.33));
CHECK_EVAL("=ROUND(5555;-1)", Value(5560));
CHECK_EVAL("=ROUND(-1.1; 0)", Value(-1));
CHECK_EVAL("=ROUND(-1.5; 0)", Value(-2));
CHECK_EVAL("=ROUND(-1.5)", Value(-2));
CHECK_EVAL("=ROUND(1.1)", Value(1));
CHECK_EVAL("=ROUND(9.8)", Value(10));
}
void TestMathFunctions::testROUNDDOWN()
{
// ODF-tests
CHECK_EVAL("=ROUNDDOWN(1.45673;2)", Value(1.45));
CHECK_EVAL("=ROUNDDOWN(1;0)", Value(1));
CHECK_EVAL("=ROUNDDOWN(1)", Value(1));
CHECK_EVAL("=ROUNDDOWN(9;-1)", Value(0));
CHECK_EVAL("=ROUNDDOWN(-9;-1)", Value(0));
CHECK_EVAL("=ROUNDDOWN(9;0)", Value(9));
CHECK_EVAL("=ROUNDDOWN(-1.1)", Value(-1));
CHECK_EVAL("=ROUNDDOWN(-1.9)", Value(-1));
}
void TestMathFunctions::testROUNDUP()
{
// ODF-tests
CHECK_EVAL("=ROUNDUP(1.45673;2)", Value(1.46));
CHECK_EVAL("=ROUNDUP(1.1;0)", Value(2));
CHECK_EVAL("=ROUNDUP(1.9;0)", Value(2));
CHECK_EVAL("=ROUNDUP(1)", Value(1));
CHECK_EVAL("=ROUNDUP(9;-1)", Value(10));
CHECK_EVAL("=ROUNDUP(-9;-1)", Value(-10));
CHECK_EVAL("=ROUNDUP(9;0)", Value(9));
CHECK_EVAL("=ROUNDUP(-1.1)", Value(-2));
CHECK_EVAL("=ROUNDUP(-1.9)", Value(-2));
}
void TestMathFunctions::testSERIESSUM()
{
CHECK_EVAL("SERIESSUM(2;0;2;{1;2})", Value(9)); //
CHECK_EVAL("SERIESSUM(2;0;2;{1;2;3;4})", Value(313)); //
CHECK_EVAL("SERIESSUM(2;0;2;{1;2;3;4;5;6;7})", Value(36409)); //
CHECK_EVAL("SERIESSUM(2;2;2;{1;6;5;4;3;2;7})", Value(127396)); //
CHECK_EVAL("SERIESSUM(3;0;2;{1;2;3;4})", Value(3178)); //
CHECK_EVAL("SERIESSUM(\"error\";0;2;{1;2})", Value::errorNUM()); // Text is not allowed
}
void TestMathFunctions::testSIGN()
{
CHECK_EVAL("SIGN(-4)", Value(-1)); // N < 0 returns -1
CHECK_EVAL("SIGN(4)", Value(1)); // N > 0 returns +1
CHECK_EVAL("SIGN(0)", Value(0)); // N == 0 returns 0
}
void TestMathFunctions::testSQRT()
{
CHECK_EVAL("SQRT(4)", Value(2)); // The square root of 4 is 2.
CHECK_EVAL("SQRT(-4)", Value::errorNUM()); // N > 0 returns +1
}
void TestMathFunctions::testSQRTPI()
{
CHECK_EVAL_SHORT("SQRTPI(1)", Value(1.77245385)); // TODO more digits / The square root of PI
CHECK_EVAL("SQRTPI(2)", Value(2.5066282746)); // The square root of 2PI
CHECK_EVAL("SQRTPI(-4)", Value::errorNUM()); // The argument must be non-negative
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSQRTPI(2)", Value(2.5066282746)); // alternate function name
}
void TestMathFunctions::testSUBTOTAL()
{
CHECK_EVAL("SUBTOTAL(1;7)", Value(7)); // Average.
CHECK_EVAL("SUBTOTAL(2;8)", Value(1)); // Count.
CHECK_EVAL("SUBTOTAL(3;11)", Value(1)); // Count.
CHECK_EVAL("SUBTOTAL(11;33)", Value(0));
CHECK_EVAL("SUBTOTAL(12;33)", Value::errorVALUE());
CHECK_EVAL("SUBTOTAL(102;8)", Value(1)); // Count.
CHECK_EVAL("SUBTOTAL(111;33)", Value(0)); // Average.
CHECK_EVAL("SUBTOTAL(1111;33)", Value(0)); // Average.
}
void TestMathFunctions::testSUMA()
{
CHECK_EVAL("SUMA(1;2;3)", Value(6)); // Simple sum.
CHECK_EVAL("SUMA(TRUE();2;3)", Value(6)); // TRUE() is 1.
}
void TestMathFunctions::testSUMIF()
{
// B3 = 7
// B4 = 2
// B5 = 3
CHECK_EVAL("SUMIF(B4:B5;\">2.5\")", Value(3)); // B4 is 2 and B5 is 3, so only B5 has a value greater than 2.5.
CHECK_EVAL("SUMIF(B3:B5;B4)", Value(2)); // Test if a cell equals the value in B4.
CHECK_EVAL("SUMIF("";B4)", Value::errorNUM()); // Constant values are not allowed for the range.
CHECK_EVAL("SUMIF(B3:B4;\"7\";B4:B5)", Value(2)); // B3 is the string "7", but its match is mapped to B4 for the summation.
CHECK_EVAL("SUMIF(B3:B10;1+1)", Value(2)); // The criteria can be an expression.
CHECK_EVAL("SUMIF(B3:B4;\"7\")", Value(0)); // TODO B3 is the string "7", but only numbers are summed.
}
void TestMathFunctions::testSUMIF_STRING()
{
m_map->calculationSettings()->setUseWildcards(false);
m_map->calculationSettings()->setUseRegularExpressions(false);
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test\";Sheet2!B1:B32767)", Value(7));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test1\";Sheet2!B1:B32767)", Value(9));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test*\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test?\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.*\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.+\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*est.*1.*\";Sheet2!B1:B32767)", Value(0));
}
void TestMathFunctions::testSUMIF_WILDCARDS()
{
m_map->calculationSettings()->setUseWildcards(true);
m_map->calculationSettings()->setUseRegularExpressions(false);
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test\";Sheet2!B1:B32767)", Value(7));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test*\";Sheet2!B1:B32767)", Value(36));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test?\";Sheet2!B1:B32767)", Value(20));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.*\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.+\";Sheet2!B1:B32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*est.*1.*\";Sheet2!B1:B32767)", Value(0));
}
void TestMathFunctions::testSUMIF_REGULAREXPRESSIONS()
{
m_map->calculationSettings()->setUseWildcards(false);
m_map->calculationSettings()->setUseRegularExpressions(true);
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test\";Sheet2!B1:B32767)", Value(7));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test*\";Sheet2!B1:B32767)", Value(7));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test?\";Sheet2!B1:B32767)", Value(7));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.*\";Sheet2!B1:B32767)", Value(36));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\"test.+\";Sheet2!B1:B32767)", Value(29));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*est.*1.*\";Sheet2!B1:B32767)", Value(28));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*\";A1:A32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*\";B1:B32767)", Value(5));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*\";Sheet2!A1:A32767)", Value(0));
CHECK_EVAL("=SUMIF(Sheet2!A1:A32767;\".*\";Sheet2!B1:B32767)", Value(91));
CHECK_EVAL("=SUMIF(A1:A32767;\".*\";A1:A32767)", Value(0));
CHECK_EVAL("=SUMIF(B1:B32767;\".+\";B1:B32767)", Value(5));
}
void TestMathFunctions::testSUMSQ()
{
CHECK_EVAL("SUMSQ(1;2;3)", Value(14)); // Simple sum.
CHECK_EVAL("SUMSQ(TRUE();2;3)", Value(14)); // TRUE() is 1.
CHECK_EVAL("SUMSQ(B4:B5)", Value(13)); // 2*2+3*3 is 13.
}
void TestMathFunctions::testTRUNC()
{
// ODF-tests
CHECK_EVAL("=TRUNC(10.1)", Value(10));
CHECK_EVAL("=TRUNC(0.5)", Value(0));
CHECK_EVAL("=TRUNC(1/3;0)", Value(0));
CHECK_EVAL("=TRUNC(1/3;1)", Value(0.3));
CHECK_EVAL("=TRUNC(1/3;2)", Value(0.33));
CHECK_EVAL("=TRUNC(1/3;2.9)", Value(0.33));
CHECK_EVAL("=TRUNC(5555;-1)", Value(5550));
CHECK_EVAL("=TRUNC(-1.1)", Value(-1));
CHECK_EVAL("=TRUNC(-1.5)", Value(-1));
}
-QTEST_KDEMAIN(TestMathFunctions, GUI)
+QTEST_MAIN(TestMathFunctions)
diff --git a/sheets/tests/TestMathFunctions.h b/sheets/tests/TestMathFunctions.h
index f63a1043b5c..d5d96ce9672 100644
--- a/sheets/tests/TestMathFunctions.h
+++ b/sheets/tests/TestMathFunctions.h
@@ -1,125 +1,124 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_MATH_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_MATH_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class Map;
class TestMathFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testABS();
void testACOS();
void testACOSH();
void testACOT();
void testACOTH();
void testASIN();
void testASINH();
void testATAN();
void testATAN2();
void testATANH();
void testBESSELI();
void testBESSELJ();
void testBESSELK();
void testBESSELY();
void testCOMBIN();
void testCOMBINA(); // to be implemented
void testCONVERT();
void testCEIL();
void testCEILING();
void testCOT();
void testCOTH();
void testDEGREES();
void testDELTA();
// void testERF(); -> TestEngineering
// void testERFC(); -> TestEngineering
void testEVEN();
void testEXP();
void testFACT();
void testFACTDOUBLE();
void testFIB();
void testFLOOR();
void testGAMMA();
void testGAMMALN();
void testGCD();
void testGESTEP();
void testINT();
void testLCM();
void testLN();
void testLOG();
void testLOG10();
void testMDETERM();
void testMINVERSE();
void testMMULT();
void testMOD();
void testMROUND();
void testMULTINOMIAL();
void testMUNIT();
void testODD();
// void testPI(); -> TestEngineering
void testPOWER();
void testPRODUCT();
void testQUOTIENT();
void testRADIANS();
void testRAND();
void testRANDBETWEEN();
void testROUND();
void testROUNDDOWN();
void testROUNDUP();
void testSERIESSUM();
void testSIGN();
void testSQRT();
void testSQRTPI();
void testSUBTOTAL();
void testSUMA();
void testSUMIF();
void testSUMIF_STRING();
void testSUMIF_WILDCARDS();
void testSUMIF_REGULAREXPRESSIONS();
void testSUMSQ();
void testTRUNC();
private:
Value TestDouble(const QString& formula, const Value& v2, int accuracy);
Value evaluate(const QString&);
Map* m_map;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_MATH_FUNCTIONS
diff --git a/sheets/tests/TestOpenFormula.cpp b/sheets/tests/TestOpenFormula.cpp
index aa78be19974..fc6335f3019 100644
--- a/sheets/tests/TestOpenFormula.cpp
+++ b/sheets/tests/TestOpenFormula.cpp
@@ -1,225 +1,225 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2005 Tomas Mecir <mecirt@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestOpenFormula.h"
#include <klocale.h>
-#include "qtest_kde.h"
+#include <QTest>
#include <Formula.h>
#include <FunctionModuleRegistry.h>
#include <Region.h>
#include <Util.h>
#include <Value.h>
using namespace Calligra::Sheets;
// because we may need to promote expected value from integer to float
#define CHECK_EVAL(x,y) { Value z(y); QCOMPARE(evaluate(x,z),(z)); }
Value TestOpenFormula::evaluate(const QString& formula, Value& ex)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
namespace QTest
{
template<>
char *toString(const Value& value)
{
QString message;
QTextStream ts(&message, QIODevice::WriteOnly);
ts << value;
return qstrdup(message.toLatin1());
}
}
#define CHECK_CONVERT(x,y) \
{ QCOMPARE(convertToOpenFormula(x),QString(y)); \
QCOMPARE(convertFromOpenFormula(y),QString(x)); }
QString TestOpenFormula::convertToOpenFormula(const QString& expr)
{
KLocale locale("en_US");
locale.setDecimalSymbol(",");
locale.setThousandsSeparator(" ");
QString formula = Odf::encodeFormula(expr, &locale);
return formula;
}
QString TestOpenFormula::convertFromOpenFormula(const QString& expr)
{
KLocale locale("en_US");
locale.setDecimalSymbol(",");
locale.setThousandsSeparator(" ");
QString formula = Odf::decodeFormula(expr, &locale);
return formula;
}
void TestOpenFormula::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
void TestOpenFormula::testEvaluation()
{
// tests from the OpenFormula testing suite:
// note that these get auto-generated using generate-openformula-tests
CHECK_EVAL("=(1/3)*3=1", Value(true)); // row 51
CHECK_EVAL("=(\"4\" & \"5\")+2", Value(47)); // row 57
CHECK_EVAL("=2+(\"4\" & \"5\")", Value(47)); // row 58
CHECK_EVAL("=1+2", Value(3)); // row 63
CHECK_EVAL("=3-1", Value(2)); // row 65
CHECK_EVAL("=5--2", Value(7)); // row 67
CHECK_EVAL("=3*4", Value(12)); // row 68
CHECK_EVAL("=2+3*4", Value(14)); // row 70
CHECK_EVAL("=6/3", Value(2)); // row 71
CHECK_EVAL("=5/2", Value(2.5)); // row 72
CHECK_EVAL("=ISERROR(1/0)", Value(true)); // row 73
CHECK_EVAL("=2^3", Value(8)); // row 74
CHECK_EVAL("=9^0.5", Value(3)); // row 75
CHECK_EVAL("=(-5)^3", Value(-125)); // row 76
CHECK_EVAL("=4^-1", Value(0.25)); // row 77
CHECK_EVAL("=5^0", Value(1)); // row 78
CHECK_EVAL("=0^5", Value(0)); // row 79
CHECK_EVAL("=2+3*4^2", Value(50)); // row 80
CHECK_EVAL("=-2^2", Value(4)); // row 81
CHECK_EVAL("=1=1", Value(true)); // row 82
CHECK_EVAL("=1=0", Value(false)); // row 84
CHECK_EVAL("=3=3.0001", Value(false)); // row 85
// Not passed for line 86.
CHECK_EVAL("=\"Hi\"=\"Bye\"", Value(false)); // row 87
CHECK_EVAL("=FALSE()=FALSE()", Value(true)); // row 88
CHECK_EVAL("=TRUE()=FALSE()", Value(false)); // row 89
CHECK_EVAL("=\"5\"=5", Value(false)); // row 90
CHECK_EVAL("=TRUE()=1", Value(false)); // row 91
// Not passed for line 92.
// Not passed for line 93.
CHECK_EVAL("=1<>1", Value(false)); // row 94
CHECK_EVAL("=1<>2", Value(true)); // row 95
CHECK_EVAL("=1<>\"1\"", Value(true)); // row 96
// Not passed for line 97.
CHECK_EVAL("=5<6", Value(true)); // row 98
CHECK_EVAL("=5<=6", Value(true)); // row 99
CHECK_EVAL("=5>6", Value(false)); // row 100
CHECK_EVAL("=5>=6", Value(false)); // row 101
CHECK_EVAL("=\"A\"<\"B\"", Value(true)); // row 102
// Not passed for line 103.
CHECK_EVAL("=\"AA\">\"A\"", Value(true)); // row 104
CHECK_EVAL("=\"Hi \" & \"there\"", Value("Hi there")); // row 107
CHECK_EVAL("=\"H\" & \"\"", Value("H")); // row 108
// Not passed for line 109.
CHECK_EVAL("=50%", Value(0.5)); // row 111
CHECK_EVAL("=20+50%", Value(20.5)); // row 112
CHECK_EVAL("=+5", Value(5)); // row 113
CHECK_EVAL("=+\"Hello\"", Value("Hello")); // row 114
CHECK_EVAL("=-\"7\"", Value(-7)); // row 116
/*
These are currently disabled, due to being locale specific.
CHECK_EVAL("=DATE(2005;1;3)=DATEVALUE(\"2005-01-03\")", Value(true)); // row 118
CHECK_EVAL("=DATE(2017.5; 1; 2)=DATEVALUE(\"2017-01-02\")", Value(true)); // row 119
CHECK_EVAL("=DATE(2006; 2.5; 3)=DATEVALUE(\"2006-02-03\")", Value(true)); // row 120
CHECK_EVAL("=DATE(2006; 1; 3.5)=DATEVALUE(\"2006-01-03\")", Value(true)); // row 121
CHECK_EVAL("=DATE(2006; 13; 3)=DATEVALUE(\"2007-01-03\")", Value(true)); // row 122
CHECK_EVAL("=DATE(2006; 1; 32)=DATEVALUE(\"2006-02-01\")", Value(true)); // row 123
CHECK_EVAL("=DATE(2006; 25; 34)=DATEVALUE(\"2008-02-03\")", Value(true)); // row 124
CHECK_EVAL("=DATE(2006;-1; 1)=DATEVALUE(\"2005-11-01\")", Value(true)); // row 125
// Not passed for line 126.
// Not passed for line 127.
CHECK_EVAL("=DATE(2004;2;29)=DATEVALUE(\"2004-02-29\")", Value(true)); // row 128
CHECK_EVAL("=DATE(2003;2;29)=DATEVALUE(\"2003-03-01\")", Value(true)); // row 129
CHECK_EVAL("=DATE(1904; 1; 1)=DATEVALUE(\"1904-01-01\")", Value(true)); // row 130
CHECK_EVAL("=DATEVALUE(\"2004-12-25\")=DATE(2004;12;25)", Value(true)); // row 131
CHECK_EVAL("=DAY(\"2006-05-21\")", Value(21)); // row 132
CHECK_EVAL("=DAY(\"5/21/2006\")", Value(21)); // row 133
CHECK_EVAL("=DAY(\"05-21-2006\")", Value(21)); // row 134
CHECK_EVAL("=DAY(\"5/21/06\")", Value(21)); // row 135
CHECK_EVAL("=DAY(\"5-21-06\")", Value(21)); // row 136
*/
}
void TestOpenFormula::testFormulaConversion()
{
// cell references
CHECK_CONVERT("=A1", "=[.A1]");
CHECK_CONVERT("=A1:A4", "=[.A1:.A4]");
CHECK_CONVERT("=A$1:$A4", "=[.A$1:.$A4]");
CHECK_CONVERT("=Sheet2!A1", "=[Sheet2.A1]");
CHECK_CONVERT("='Sheet 2'!A1", "=['Sheet 2'.A1]");
CHECK_CONVERT("=Sheet2!A1:B4", "=[Sheet2.A1:Sheet2.B4]");
CHECK_CONVERT("='Sheet 2'!A1:B4", "=['Sheet 2'.A1:'Sheet 2'.B4]");
// equality
CHECK_CONVERT("=A1==A2", "=[.A1]=[.A2]");
// strings
CHECK_CONVERT("=\"2,2\"+2,1+\"2,0\"", "=\"2,2\"+2.1+\"2,0\"");
// decimal separator ','
CHECK_CONVERT("=,12", "=.12");
CHECK_CONVERT("=12,12", "=12.12");
CHECK_CONVERT("=368*7*(0,1738+0,1784)*(0,1738+0,1784)", "=368*7*(0.1738+0.1784)*(0.1738+0.1784)");
// function names
CHECK_CONVERT("=sum(A1;A2;A3;A4;A5)", "=sum([.A1];[.A2];[.A3];[.A4];[.A5])");
}
void TestOpenFormula::testReferenceLoading()
{
QCOMPARE(Region::loadOdf(".A1"), QString("A1"));
QCOMPARE(Region::loadOdf(".A1:.A4"), QString("A1:A4"));
QCOMPARE(Region::loadOdf(".A$1:.$A4"), QString("A$1:$A4"));
QCOMPARE(Region::loadOdf("Sheet2.A1"), QString("Sheet2!A1"));
QCOMPARE(Region::loadOdf("'Sheet 2'.A1"), QString("'Sheet 2'!A1"));
QCOMPARE(Region::loadOdf("Sheet2.A1:Sheet2.B4"), QString("Sheet2!A1:B4"));
QCOMPARE(Region::loadOdf("'Sheet 2'.A1:'Sheet 2'.B4"), QString("'Sheet 2'!A1:B4"));
QCOMPARE(Region::loadOdf("$Sheet2.A1:$Sheet2.B4"), QString("Sheet2!A1:B4"));
QCOMPARE(Region::loadOdf("$'Sheet 2'.A1:$'Sheet 2'.B4"), QString("'Sheet 2'!A1:B4"));
}
void TestOpenFormula::testReferenceSaving()
{
QCOMPARE(Region::saveOdf("A1"), QString(".A1"));
QCOMPARE(Region::saveOdf("A1:A4"), QString(".A1:.A4"));
QCOMPARE(Region::saveOdf("A$1:$A4"), QString(".A$1:.$A4"));
QCOMPARE(Region::saveOdf("Sheet2!A1"), QString("Sheet2.A1"));
QCOMPARE(Region::saveOdf("'Sheet 2'!A1"), QString("'Sheet 2'.A1"));
QCOMPARE(Region::saveOdf("Sheet2!A1:B4"), QString("Sheet2.A1:Sheet2.B4"));
QCOMPARE(Region::saveOdf("'Sheet 2'!A1:B4"), QString("'Sheet 2'.A1:'Sheet 2'.B4"));
}
-QTEST_KDEMAIN(TestOpenFormula, GUI)
+QTEST_MAIN(TestOpenFormula)
diff --git a/sheets/tests/TestOpenFormula.h b/sheets/tests/TestOpenFormula.h
index 68abfee2550..dde7bcc9aa8 100644
--- a/sheets/tests/TestOpenFormula.h
+++ b/sheets/tests/TestOpenFormula.h
@@ -1,54 +1,53 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2005 Tomas Mecir <mecirt@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_OPENFORMULA
#define CALLIGRA_SHEETS_TEST_OPENFORMULA
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class Value;
class TestOpenFormula: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testEvaluation();
void testFormulaConversion();
void testReferenceLoading();
void testReferenceSaving();
private:
Value evaluate(const QString&, Value&);
QString convertToOpenFormula(const QString& expr);
QString convertFromOpenFormula(const QString& expr);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_OPENFORMULA
diff --git a/sheets/tests/TestPasteCommand.cpp b/sheets/tests/TestPasteCommand.cpp
index 7eaad48c946..0cc12a03725 100644
--- a/sheets/tests/TestPasteCommand.cpp
+++ b/sheets/tests/TestPasteCommand.cpp
@@ -1,67 +1,67 @@
/* This file is part of the KDE project
Copyright 2012 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestPasteCommand.h"
-#include <qtest_kde.h>
+#include <QTest>
#include <KoPart.h>
#include "part/CanvasItem.h"
#include "part/Doc.h"
#include "Map.h"
#include "ui/Selection.h"
#include "Sheet.h"
#include <Value.h>
#include "commands/PasteCommand.h"
using namespace Calligra::Sheets;
void PasteCommandTest::testKSpreadSnippet()
{
Doc doc(new MockPart);
Map *map = doc.map();
Sheet* sheet = new Sheet(map, "Sheet1");
map->addSheet(sheet);
CanvasItem canvas(&doc);
Selection selection(&canvas);
selection.setActiveSheet(sheet);
selection.initialize(QPoint(2, 4), sheet);
QMimeData *mimedata = new QMimeData();
mimedata->setData("application/x-kspread-snippet",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE spreadsheet-snippet>\n"
"<spreadsheet-snippet rows=\"1\" columns=\"1\">\n"
" <cell row=\"1\" column=\"1\">\n"
" <text outStr=\"3\" dataType=\"Num\">3</text>\n"
" </cell>\n"
"</spreadsheet-snippet>\n");
PasteCommand *command = new PasteCommand();
command->setSheet(selection.activeSheet());
command->add(selection);
command->setMimeData(mimedata);
command->setPasteFC(true);
command->execute(&canvas);
kDebug() << Cell(sheet, 2, 4).value() << Cell(sheet, 1, 3).value() << Value(3);
QCOMPARE(Cell(sheet, 2, 4).value(), Value(3));
}
-QTEST_KDEMAIN(PasteCommandTest, GUI)
+QTEST_MAIN(PasteCommandTest)
diff --git a/sheets/tests/TestPasteCommand.h b/sheets/tests/TestPasteCommand.h
index f3594ddac33..d58c211b130 100644
--- a/sheets/tests/TestPasteCommand.h
+++ b/sheets/tests/TestPasteCommand.h
@@ -1,40 +1,40 @@
/* This file is part of the KDE project
Copyright 2012 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_PASTE_COMMAND_TEST
#define CALLIGRA_SHEETS_PASTE_COMMAND_TEST
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class PasteCommandTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testKSpreadSnippet();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_PASTE_COMMAND_TEST
diff --git a/sheets/tests/TestPointStorage.cpp b/sheets/tests/TestPointStorage.cpp
index 8627f3e6798..47c51607a9c 100644
--- a/sheets/tests/TestPointStorage.cpp
+++ b/sheets/tests/TestPointStorage.cpp
@@ -1,1199 +1,1201 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#define KS_colMax 10
#define KS_rowMax 10
#include "PointStorage.h"
#include "TestPointStorage.h"
+#include <QTest>
+
using namespace Calligra::Sheets;
void PointStorageTest::testInsertion()
{
PointStorage<int> storage;
// int counter = 1;
// for ( int col = 1; col <= 5; ++col )
// for ( int row = 1; row <= 5; ++row )
// cout << qPrintable( QString( "storage.insert( %1, %2, %3 );" ).arg(col,2).arg(row,2).arg(counter++,2) ) << endl; storage.insert( 1, 1, 1 );
storage.insert(1, 1, 1);
storage.insert(1, 2, 2);
storage.insert(1, 3, 3);
storage.insert(1, 4, 4);
storage.insert(1, 5, 5);
storage.insert(2, 1, 6);
storage.insert(2, 2, 7);
storage.insert(2, 3, 8);
storage.insert(2, 4, 9);
storage.insert(2, 5, 10);
storage.insert(3, 1, 11);
storage.insert(3, 2, 12);
storage.insert(3, 3, 13);
storage.insert(3, 4, 14);
storage.insert(3, 5, 15);
storage.insert(4, 1, 16);
storage.insert(4, 2, 17);
storage.insert(4, 3, 18);
storage.insert(4, 4, 19);
storage.insert(4, 5, 20);
storage.insert(5, 1, 21);
storage.insert(5, 2, 22);
storage.insert(5, 3, 23);
storage.insert(5, 4, 24);
storage.insert(5, 5, 25);
// overwrite
int old = storage.insert(5, 5, 30);
QCOMPARE(old, 25);
// ( 1, 6,11,16,21)
// ( 2, 7,12,17,22)
// ( 3, 8,13,18,23)
// ( 4, 9,14,19,24)
// ( 5,10,15,20,25)
// qDebug() << endl << qPrintable( storage.dump() );
QVector<int> data(QVector<int>() << 1 << 6 << 11 << 16 << 21
<< 2 << 7 << 12 << 17 << 22
<< 3 << 8 << 13 << 18 << 23
<< 4 << 9 << 14 << 19 << 24
<< 5 << 10 << 15 << 20 << 30);
QVector<int> rows(QVector<int>() << 0 << 5 << 10 << 15 << 20);
QVector<int> cols(QVector<int>() << 1 << 2 << 3 << 4 << 5
<< 1 << 2 << 3 << 4 << 5
<< 1 << 2 << 3 << 4 << 5
<< 1 << 2 << 3 << 4 << 5
<< 1 << 2 << 3 << 4 << 5);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// qDebug() << "data result: " << storage.m_data;
// qDebug() << "data expect: " << data;
// qDebug() << "rows result: " << storage.m_rows;
// qDebug() << "rows expect: " << rows;
// qDebug() << "cols result: " << storage.m_cols;
// qDebug() << "cols expect: " << cols;
// reverse filling
storage.clear();
storage.insert(2, 2, 4);
storage.insert(1, 2, 3);
storage.insert(2, 1, 2);
storage.insert(1, 1, 1);
// ( 1, 2)
// ( 3, 4)
data = QVector<int>() << 1 << 2 << 3 << 4;
rows = QVector<int>() << 0 << 2;
cols = QVector<int>() << 1 << 2 << 1 << 2;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testLookup()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QCOMPARE(storage.lookup(1, 1), 1);
QCOMPARE(storage.lookup(1, 2), 4);
QCOMPARE(storage.lookup(1, 3), 0);
QCOMPARE(storage.lookup(1, 4), 0);
QCOMPARE(storage.lookup(1, 5), 11);
QCOMPARE(storage.lookup(2, 1), 2);
QCOMPARE(storage.lookup(2, 2), 5);
QCOMPARE(storage.lookup(2, 3), 7);
QCOMPARE(storage.lookup(2, 4), 0);
QCOMPARE(storage.lookup(2, 5), 0);
QCOMPARE(storage.lookup(3, 1), 0);
QCOMPARE(storage.lookup(3, 2), 6);
QCOMPARE(storage.lookup(3, 3), 8);
QCOMPARE(storage.lookup(3, 4), 0);
QCOMPARE(storage.lookup(3, 5), 0);
QCOMPARE(storage.lookup(4, 1), 0);
QCOMPARE(storage.lookup(4, 2), 0);
QCOMPARE(storage.lookup(4, 3), 0);
QCOMPARE(storage.lookup(4, 4), 10);
QCOMPARE(storage.lookup(4, 5), 0);
QCOMPARE(storage.lookup(5, 1), 3);
QCOMPARE(storage.lookup(5, 2), 0);
QCOMPARE(storage.lookup(5, 3), 9);
QCOMPARE(storage.lookup(5, 4), 0);
QCOMPARE(storage.lookup(5, 5), 12);
// empty row checking
storage.clear();
storage.insert(1, 1, 1);
// ( 1)
QCOMPARE(storage.lookup(1, 1), 1);
QCOMPARE(storage.lookup(2, 1), 0);
QCOMPARE(storage.lookup(1, 2), 0);
QCOMPARE(storage.lookup(2, 2), 0);
}
void PointStorageTest::testDeletion()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int old = storage.take(4, 4);
QCOMPARE(old, 10);
old = storage.take(5, 1);
QCOMPARE(old, 3);
old = storage.take(2, 2);
QCOMPARE(old, 5);
// ( 1, 2, , , )
// ( 4, , 6, , )
// ( , 7, 8, , 9)
// ( , , , , )
// (11, , , ,12)
const QVector<int> data(QVector<int>() << 1 << 2 << 4 << 6 << 7 << 8 << 9 << 11 << 12);
const QVector<int> rows(QVector<int>() << 0 << 2 << 4 << 7 << 7);
const QVector<int> cols(QVector<int>() << 1 << 2 << 1 << 3 << 2 << 3 << 5 << 1 << 5);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// empty row checking
storage.clear();
storage.insert(1, 1, 1);
// ( 1)
old = storage.take(2, 2);
QCOMPARE(old, 0);
old = storage.take(1, 2);
QCOMPARE(old, 0);
old = storage.take(2, 1);
QCOMPARE(old, 0);
old = storage.take(1, 1);
QCOMPARE(old, 1);
QCOMPARE(storage.lookup(1, 1), 0);
QCOMPARE(storage.lookup(2, 1), 0);
QCOMPARE(storage.lookup(1, 2), 0);
QCOMPARE(storage.lookup(2, 2), 0);
}
void PointStorageTest::testInsertColumns()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.insertColumns(2, 2); // in the middle
QVERIFY(old.count() == 0);
old = storage.insertColumns(9, 1); // beyond the current end
QVERIFY(old.count() == 0);
// ( 1, , , 2, , , 3)
// ( 4, , , 5, 6, , )
// ( , , , 7, 8, , 9)
// ( , , , , ,10, )
// (11, , , , , ,12)
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12);
QVector<int> rows(QVector<int>() << 0 << 3 << 6 << 9 << 10);
QVector<int> cols(QVector<int>() << 1 << 4 << 7 << 1 << 4 << 5 << 4 << 5 << 7 << 6 << 1 << 7);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
old = storage.insertColumns(6, 4); // shift the last column out of range
QVERIFY(old.count() == 3);
QVERIFY(old.contains(qMakePair(QPoint(7, 1), 3)));
QVERIFY(old.contains(qMakePair(QPoint(7, 3), 9)));
QVERIFY(old.contains(qMakePair(QPoint(7, 5), 12)));
// ( 1, , , 2, , , , , , )
// ( 4, , , 5, 6, , , , , )
// ( , , , 7, 8, , , , , )
// ( , , , , , , , , ,10)
// (11, , , , , , , , , )
data = QVector<int>() << 1 << 2 << 4 << 5 << 6 << 7 << 8 << 10 << 11;
rows = QVector<int>() << 0 << 2 << 5 << 7 << 8;
cols = QVector<int>() << 1 << 4 << 1 << 4 << 5 << 4 << 5 << 10 << 1;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// empty row checking
storage.clear();
storage.insert(1, 1, 1);
// ( 1)
old = storage.insertColumns(1, 1);
QVERIFY(old.count() == 0);
// ( , 1)
data = QVector<int>() << 1;
rows = QVector<int>() << 0;
cols = QVector<int>() << 2;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testDeleteColumns()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.removeColumns(2, 2); // in the middle
QVERIFY(old.count() == 5);
QVERIFY(old.contains(qMakePair(QPoint(2, 1), 2)));
QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5)));
QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6)));
QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7)));
QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8)));
old = storage.removeColumns(3, 1); // beyond the current end
QVERIFY(old.count() == 3);
QVERIFY(old.contains(qMakePair(QPoint(3, 1), 3)));
QVERIFY(old.contains(qMakePair(QPoint(3, 3), 9)));
QVERIFY(old.contains(qMakePair(QPoint(3, 5), 12)));
// ( 1, )
// ( 4, )
// ( , )
// ( ,10)
// (11, )
const QVector<int> data(QVector<int>() << 1 << 4 << 10 << 11);
const QVector<int> rows(QVector<int>() << 0 << 1 << 2 << 2 << 3);
const QVector<int> cols(QVector<int>() << 1 << 1 << 2 << 1);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testInsertRows()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.insertRows(2, 2); // in the middle
QVERIFY(old.count() == 0);
old = storage.insertRows(9, 1); // beyond the current end
QVERIFY(old.count() == 0);
// ( 1, 2, , , 3)
// ( , , , , )
// ( , , , , )
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12);
QVector<int> rows(QVector<int>() << 0 << 3 << 3 << 3 << 6 << 9 << 10);
QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
old = storage.insertRows(6, 4); // shift the last row out of range
QVERIFY(old.count() == 2);
QVERIFY(old.contains(qMakePair(QPoint(1, 7), 11)));
QVERIFY(old.contains(qMakePair(QPoint(5, 7), 12)));
// ( 1, 2, , , 3)
// ( , , , , )
// ( , , , , )
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , , , )
// ( , , , , )
// ( , , , , )
// ( , , , , )
// ( , , ,10, )
data = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10;
rows = QVector<int>() << 0 << 3 << 3 << 3 << 6 << 9 << 9 << 9 << 9 << 9;
cols = QVector<int>() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// first row checking
storage.clear();
storage.insert(1, 1, 1);
// ( 1)
old = storage.insertRows(1, 1);
QVERIFY(old.count() == 0);
// ( )
// ( 1)
data = QVector<int>() << 1;
rows = QVector<int>() << 0 << 0;
cols = QVector<int>() << 1;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testDeleteRows()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.removeRows(2, 2); // in the middle
QVERIFY(old.count() == 6);
QVERIFY(old.contains(qMakePair(QPoint(1, 2), 4)));
QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5)));
QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6)));
QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7)));
QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8)));
QVERIFY(old.contains(qMakePair(QPoint(5, 3), 9)));
old = storage.removeRows(3, 1); // at the current end
QVERIFY(old.count() == 2);
QVERIFY(old.contains(qMakePair(QPoint(1, 3), 11)));
QVERIFY(old.contains(qMakePair(QPoint(5, 3), 12)));
// ( 1, 2, , , 3)
// ( , , ,10, )
const QVector<int> data(QVector<int>() << 1 << 2 << 3 << 10);
const QVector<int> rows(QVector<int>() << 0 << 3);
const QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 4);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testShiftLeft()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.removeShiftLeft(QRect(2, 2, 2, 2));
QVERIFY(old.count() == 4);
QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5)));
QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6)));
QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7)));
QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8)));
old = storage.removeShiftLeft(QRect(5, 5, 1, 1));
QVERIFY(old.count() == 1);
QVERIFY(old.contains(qMakePair(QPoint(5, 5), 12)));
// ( 1, 2, , , 3)
// ( 4, , , , )
// ( , , 9, , )
// ( , , ,10, )
// (11, , , , )
const QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 9 << 10 << 11);
const QVector<int> rows(QVector<int>() << 0 << 3 << 4 << 5 << 6);
const QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 1 << 3 << 4 << 1);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testShiftRight()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.insertShiftRight(QRect(2, 2, 2, 2));
QVERIFY(old.count() == 0);
old = storage.insertShiftRight(QRect(5, 5, 1, 1));
QVERIFY(old.count() == 0);
// ( 1, 2, , , 3, , )
// ( 4, , , 5, 6, , )
// ( , , , 7, 8, , 9)
// ( , , ,10, , , )
// (11, , , , ,12, )
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12);
QVector<int> rows(QVector<int>() << 0 << 3 << 6 << 9 << 10);
QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 1 << 4 << 5 << 4 << 5 << 7 << 4 << 1 << 6);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
old = storage.insertShiftRight(QRect(4, 2, 6, 1)); // shift the 6 out of range
QVERIFY(old.count() == 1);
QVERIFY(old.contains(qMakePair(QPoint(5, 2), 6)));
// ( 1, 2, , , 3, , , , , )
// ( 4, , , , , , , , , 5)
// ( , , , 7, 8, , 9, , , )
// ( , , ,10, , , , , , )
// (11, , , , ,12, , , , )
data = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 7 << 8 << 9 << 10 << 11 << 12;
rows = QVector<int>() << 0 << 3 << 5 << 8 << 9;
cols = QVector<int>() << 1 << 2 << 5 << 1 << 10 << 4 << 5 << 7 << 4 << 1 << 6;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testShiftUp()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.removeShiftUp(QRect(2, 2, 2, 1));
QVERIFY(old.count() == 2);
QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5)));
QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6)));
old = storage.removeShiftUp(QRect(5, 5, 1, 1));
QVERIFY(old.count() == 1);
QVERIFY(old.contains(qMakePair(QPoint(5, 5), 12)));
// ( 1, 2, , , 3)
// ( 4, 7, 8, , )
// ( , , , , 9)
// ( , , ,10, )
// (11, , , , )
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 7 << 8 << 9 << 10 << 11);
QVector<int> rows(QVector<int>() << 0 << 3 << 6 << 7 << 8);
QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 1 << 2 << 3 << 5 << 4 << 1);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// first row
storage.clear();
storage.m_data << 1 << 2 << 3 << 4;
storage.m_rows << 0 << 1 << 3;
storage.m_cols << 1 << 1 << 2 << 2;
// ( 1, )
// ( 2, 3)
// ( , 4)
old = storage.removeShiftUp(QRect(1, 1, 2, 2));
QVERIFY(old.count() == 3);
QVERIFY(old.contains(qMakePair(QPoint(1, 1), 1)));
QVERIFY(old.contains(qMakePair(QPoint(1, 2), 2)));
QVERIFY(old.contains(qMakePair(QPoint(2, 2), 3)));
// ( , 4)
data = QVector<int>() << 4;
rows = QVector<int>() << 0;
cols = QVector<int>() << 2;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testShiftDown()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector< QPair<QPoint, int> > old;
old = storage.insertShiftDown(QRect(2, 2, 2, 2));
QVERIFY(old.count() == 0);
old = storage.insertShiftDown(QRect(5, 5, 1, 1));
QVERIFY(old.count() == 0);
// ( 1, 2, , , 3)
// ( 4, , , , )
// ( , , , , 9)
// ( , 5, 6,10, )
// (11, 7, 8, , )
// ( , , , ,12)
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 9 << 5 << 6 << 10 << 11 << 7 << 8 << 12);
QVector<int> rows(QVector<int>() << 0 << 3 << 4 << 5 << 8 << 11);
QVector<int> cols(QVector<int>() << 1 << 2 << 5 << 1 << 5 << 2 << 3 << 4 << 1 << 2 << 3 << 5);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
old = storage.insertShiftDown(QRect(2, 4, 1, 6)); // shift the 7 out of range
QVERIFY(old.count() == 1);
QVERIFY(old.contains(qMakePair(QPoint(2, 5), 7)));
// ( 1, 2, , , 3)
// ( 4, , , , )
// ( , , , , 9)
// ( , , 6,10, )
// (11, , 8, , )
// ( , , , ,12)
// ( , , , , )
// ( , , , , )
// ( , , , , )
// ( , , , , )
// ( , 5, , , )
data = QVector<int>() << 1 << 2 << 3 << 4 << 9 << 6 << 10 << 11 << 8 << 12 << 5;
rows = QVector<int>() << 0 << 3 << 4 << 5 << 7 << 9 << 10 << 10 << 10 << 10;
cols = QVector<int>() << 1 << 2 << 5 << 1 << 5 << 3 << 4 << 1 << 3 << 5 << 2;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
// first row
storage.clear();
storage.m_data << 1 << 2 << 3 << 4;
storage.m_rows << 0 << 1 << 3;
storage.m_cols << 1 << 1 << 2 << 2;
// ( 1, )
// ( 2, 3)
// ( , 4)
old = storage.insertShiftDown(QRect(1, 1, 2, 2));
QVERIFY(old.count() == 0);
// ( , )
// ( , )
// ( 1, )
// ( 2, 3)
// ( , 4)
data = QVector<int>() << 1 << 2 << 3 << 4;
rows = QVector<int>() << 0 << 0 << 0 << 1 << 3;
cols = QVector<int>() << 1 << 1 << 2 << 2;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testShiftDownUp()
{
PointStorage<int> storage;
for (int row = 1; row < 6; ++row) {
for (int col = 1; col < 6; ++col) {
storage.m_data << (row * col);
storage.m_cols << col;
}
storage.m_rows << (5 *(row - 1));
}
// qDebug() << "Origin:" << endl << qPrintable(storage.dump());
// ( 1, 2, 3, 4, 5)
// ( 2, 4, 6, 8,10)
// ( 3, 6, 8,12,15)
// ( 4, 8,12,16,20)
// ( 5,10,15,20,25)
QVector<int> data(QVector<int>() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 6 << 8 << 10 << 3 << 6 << 9 << 12 << 15 << 4 << 8 << 12 << 16 << 20 << 5 << 10 << 15 << 20 << 25);
QVector<int> rows(QVector<int>() << 0 << 5 << 10 << 15 << 20);
QVector<int> cols(QVector<int>() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5);
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
QVector< QPair<QPoint, int> > old;
old = storage.insertShiftDown(QRect(3, 2, 2, 2));
QVERIFY(old.count() == 0);
// qDebug() << endl << qPrintable(storage.dump());
// ( 1, 2, 3, 4, 5)
// ( 2, 4, , ,10)
// ( 3, 6, , ,15)
// ( 4, 8, 6, 8,20)
// ( 5,10, 9,12,25)
// ( , ,12,16, )
// ( , ,15,20, )
data = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 10 << 3 << 6 << 15 << 4 << 8 << 6 << 8 << 20 << 5 << 10 << 9 << 12 << 25 << 12 << 16 << 15 << 20;
cols = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 5 << 1 << 2 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 3 << 4 << 3 << 4;
rows = QVector<int>() << 0 << 5 << 8 << 11 << 16 << 21 << 23;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
old = storage.removeShiftUp(QRect(3, 2, 2, 2));
QVERIFY(old.count() == 0);
data = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 6 << 8 << 10 << 3 << 6 << 9 << 12 << 15 << 4 << 8 << 12 << 16 << 20 << 5 << 10 << 15 << 20 << 25;
rows = QVector<int>() << 0 << 5 << 10 << 15 << 20;
cols = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5;
QCOMPARE(storage.m_data, data);
QCOMPARE(storage.m_rows, rows);
QCOMPARE(storage.m_cols, cols);
}
void PointStorageTest::testFirstInColumn()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newRow = 0;
QCOMPARE(storage.firstInColumn(1, &newRow), 1);
QCOMPARE(newRow, 1);
QCOMPARE(storage.firstInColumn(2, &newRow), 2);
QCOMPARE(newRow, 1);
QCOMPARE(storage.firstInColumn(3, &newRow), 6);
QCOMPARE(newRow, 2);
QCOMPARE(storage.firstInColumn(4, &newRow), 10);
QCOMPARE(newRow, 4);
QCOMPARE(storage.firstInColumn(5, &newRow), 3);
QCOMPARE(newRow, 1);
storage.clear();
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 9;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , , , )
// (11, , , ,12)
QCOMPARE(storage.firstInColumn(4, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.firstInColumn(5, &newRow), 3);
QCOMPARE(newRow, 1);
}
void PointStorageTest::testFirstInRow()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newCol = 0;
QCOMPARE(storage.firstInRow(1, &newCol), 1);
QCOMPARE(newCol, 1);
QCOMPARE(storage.firstInRow(2, &newCol), 4);
QCOMPARE(newCol, 1);
QCOMPARE(storage.firstInRow(3, &newCol), 7);
QCOMPARE(newCol, 2);
QCOMPARE(storage.firstInRow(4, &newCol), 10);
QCOMPARE(newCol, 4);
QCOMPARE(storage.firstInRow(5, &newCol), 11);
QCOMPARE(newCol, 1);
storage.clear();
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 9;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , , , )
// (11, , , ,12)
QCOMPARE(storage.firstInRow(4, &newCol), 0);
QCOMPARE(newCol, 0);
QCOMPARE(storage.firstInRow(5, &newCol), 11);
QCOMPARE(newCol, 1);
storage.clear();
storage.m_data << 1;
storage.m_rows << 0 << 0;
storage.m_cols << 1;
// ( )
// ( 1)
QCOMPARE(storage.firstInRow(1, &newCol), 0);
QCOMPARE(newCol, 0);
QCOMPARE(storage.firstInRow(2, &newCol), 1);
QCOMPARE(newCol, 1);
}
void PointStorageTest::testLastInColumn()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newRow = 0;
QCOMPARE(storage.lastInColumn(1, &newRow), 11);
QCOMPARE(newRow, 5);
QCOMPARE(storage.lastInColumn(2, &newRow), 7);
QCOMPARE(newRow, 3);
QCOMPARE(storage.lastInColumn(3, &newRow), 8);
QCOMPARE(newRow, 3);
QCOMPARE(storage.lastInColumn(4, &newRow), 10);
QCOMPARE(newRow, 4);
QCOMPARE(storage.lastInColumn(5, &newRow), 12);
QCOMPARE(newRow, 5);
QCOMPARE(storage.lastInColumn(6, &newRow), 0);
QCOMPARE(newRow, 0);
}
void PointStorageTest::testLastInRow()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newCol = 0;
QCOMPARE(storage.lastInRow(1, &newCol), 3);
QCOMPARE(newCol, 5);
QCOMPARE(storage.lastInRow(2, &newCol), 6);
QCOMPARE(newCol, 3);
QCOMPARE(storage.lastInRow(3, &newCol), 9);
QCOMPARE(newCol, 5);
QCOMPARE(storage.lastInRow(4, &newCol), 10);
QCOMPARE(newCol, 4);
QCOMPARE(storage.lastInRow(5, &newCol), 12);
QCOMPARE(newCol, 5);
QCOMPARE(storage.lastInRow(6, &newCol), 0);
QCOMPARE(newCol, 0);
}
void PointStorageTest::testNextInColumn()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newRow = 0;
QCOMPARE(storage.nextInColumn(1, 3, &newRow), 11);
QCOMPARE(newRow, 5);
QCOMPARE(storage.nextInColumn(2, 3, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.nextInColumn(3, 3, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.nextInColumn(4, 3, &newRow), 10);
QCOMPARE(newRow, 4);
QCOMPARE(storage.nextInColumn(5, 3, &newRow), 12);
QCOMPARE(newRow, 5);
QCOMPARE(storage.nextInColumn(6, 3, &newRow), 0);
QCOMPARE(newRow, 0);
//
storage.clear();
storage.m_data << 1 << 2 << 3 << 4;
storage.m_rows << 0 << 0 << 0 << 1 << 2;
storage.m_cols << 1 << 1 << 1 << 2;
// ( , )
// ( , )
// ( 1, )
// ( 2, )
// ( 3, 4)
QCOMPARE(storage.nextInColumn(1, 1, &newRow), 1);
QCOMPARE(newRow, 3);
QCOMPARE(storage.nextInColumn(2, 1, &newRow), 4);
QCOMPARE(newRow, 5);
QCOMPARE(storage.nextInColumn(1, 7, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.nextInColumn(2, 5, &newRow), 0);
QCOMPARE(newRow, 0);
}
void PointStorageTest::testNextInRow()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newCol = 0;
QCOMPARE(storage.nextInRow(3, 1, &newCol), 3);
QCOMPARE(newCol, 5);
QCOMPARE(storage.nextInRow(3, 2, &newCol), 0);
QCOMPARE(newCol, 0);
QCOMPARE(storage.nextInRow(3, 3, &newCol), 9);
QCOMPARE(newCol, 5);
QCOMPARE(storage.nextInRow(3, 4, &newCol), 10);
QCOMPARE(newCol, 4);
QCOMPARE(storage.nextInRow(3, 5, &newCol), 12);
QCOMPARE(newCol, 5);
QCOMPARE(storage.nextInRow(3, 6, &newCol), 0);
QCOMPARE(newCol, 0);
}
void PointStorageTest::testPrevInColumn()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newRow = 0;
QCOMPARE(storage.prevInColumn(1, 3, &newRow), 4);
QCOMPARE(newRow, 2);
QCOMPARE(storage.prevInColumn(2, 3, &newRow), 5);
QCOMPARE(newRow, 2);
QCOMPARE(storage.prevInColumn(3, 3, &newRow), 6);
QCOMPARE(newRow, 2);
QCOMPARE(storage.prevInColumn(4, 3, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.prevInColumn(5, 3, &newRow), 3);
QCOMPARE(newRow, 1);
QCOMPARE(storage.prevInColumn(6, 3, &newRow), 0);
QCOMPARE(newRow, 0);
QCOMPARE(storage.prevInColumn(3, 7, &newRow), 8);
QCOMPARE(newRow, 3);
}
void PointStorageTest::testPrevInRow()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
int newCol = 0;
QCOMPARE(storage.prevInRow(3, 1, &newCol), 2);
QCOMPARE(newCol, 2);
QCOMPARE(storage.prevInRow(3, 2, &newCol), 5);
QCOMPARE(newCol, 2);
QCOMPARE(storage.prevInRow(3, 3, &newCol), 7);
QCOMPARE(newCol, 2);
QCOMPARE(storage.prevInRow(3, 4, &newCol), 0);
QCOMPARE(newCol, 0);
QCOMPARE(storage.prevInRow(3, 5, &newCol), 11);
QCOMPARE(newCol, 1);
QCOMPARE(storage.prevInRow(3, 6, &newCol), 0);
QCOMPARE(newCol, 0);
}
void PointStorageTest::testIteration()
{
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QCOMPARE(storage.data(0), 1);
QCOMPARE(storage.data(1), 2);
QCOMPARE(storage.data(2), 3);
QCOMPARE(storage.data(3), 4);
QCOMPARE(storage.data(4), 5);
QCOMPARE(storage.data(5), 6);
QCOMPARE(storage.data(6), 7);
QCOMPARE(storage.data(7), 8);
QCOMPARE(storage.data(8), 9);
QCOMPARE(storage.data(9), 10);
QCOMPARE(storage.data(10), 11);
QCOMPARE(storage.data(11), 12);
QCOMPARE(storage.col(0), 1);
QCOMPARE(storage.col(1), 2);
QCOMPARE(storage.col(2), 5);
QCOMPARE(storage.col(3), 1);
QCOMPARE(storage.col(4), 2);
QCOMPARE(storage.col(5), 3);
QCOMPARE(storage.col(6), 2);
QCOMPARE(storage.col(7), 3);
QCOMPARE(storage.col(8), 5);
QCOMPARE(storage.col(9), 4);
QCOMPARE(storage.col(10), 1);
QCOMPARE(storage.col(11), 5);
QCOMPARE(storage.row(0), 1);
QCOMPARE(storage.row(1), 1);
QCOMPARE(storage.row(2), 1);
QCOMPARE(storage.row(3), 2);
QCOMPARE(storage.row(4), 2);
QCOMPARE(storage.row(5), 2);
QCOMPARE(storage.row(6), 3);
QCOMPARE(storage.row(7), 3);
QCOMPARE(storage.row(8), 3);
QCOMPARE(storage.row(9), 4);
QCOMPARE(storage.row(10), 5);
QCOMPARE(storage.row(11), 5);
}
void PointStorageTest::testColumnIteration()
{
PointStorage<int> storage;
storage.insert(1, 1, 27);
int row = -1;
QCOMPARE(storage.firstInColumn(1, &row), 27);
QCOMPARE(row, 1);
row = -1;
// QCOMPARE(storage.nextInColumn(1, 0, &row), 27);
// QCOMPARE(row, 1);
QCOMPARE(storage.nextInColumn(1, 1, &row), 0);
QCOMPARE(row, 0);
storage.insert(1, 5, 5);
QCOMPARE(storage.nextInColumn(1, 1, &row), 5);
QCOMPARE(row, 5);
QCOMPARE(storage.nextInColumn(5, 1, &row), 0);
QCOMPARE(row, 0);
row = -1;
QCOMPARE(storage.nextInColumn(6, 1, &row), 0);
QCOMPARE(row, 0);
// reverse iteration
QCOMPARE(storage.lastInColumn(1, &row), 5);
QCOMPARE(row, 5);
row = -1;
// QCOMPARE(storage.prevInColumn(1, KS_rowMax + 1, &row), 5);
// QCOMPARE(row, 5);
QCOMPARE(storage.prevInColumn(1, 6, &row), 5);
QCOMPARE(row, 5);
QCOMPARE(storage.prevInColumn(1, 5, &row), 27);
QCOMPARE(row, 1);
QCOMPARE(storage.prevInColumn(1, 1, &row), 0);
QCOMPARE(row, 0);
}
void PointStorageTest::testRowIteration()
{
PointStorage<int> storage;
storage.insert(1, 1, 27);
int col = -1;
QCOMPARE(storage.firstInRow(1, &col), 27);
QCOMPARE(col, 1);
col = -1;
QCOMPARE(storage.nextInRow(1, 1, &col), 0);
QCOMPARE(col, 0);
storage.insert(5, 1, 5);
QCOMPARE(storage.nextInRow(1, 1, &col), 5);
QCOMPARE(col, 5);
QCOMPARE(storage.nextInRow(1, 5, &col), 0);
QCOMPARE(col, 0);
col = -1;
QCOMPARE(storage.nextInRow(1, 6, &col), 0);
QCOMPARE(col, 0);
// reverse iteration
QEXPECT_FAIL("", "Will fix in the next release", Continue);
QCOMPARE(storage.lastInRow(1, &col), 5);
QEXPECT_FAIL("", "Will fix in the next release", Continue);
QCOMPARE(col, 5);
col = -1;
// QCOMPARE(storage.prevInRow(KS_colMax + 1, 1, &col), 5);
// QCOMPARE(col, 5);
QCOMPARE(storage.prevInRow(6, 1, &col), 5);
QCOMPARE(col, 5);
QCOMPARE(storage.prevInRow(5, 1, &col), 27);
QCOMPARE(col, 1);
QCOMPARE(storage.prevInRow(1, 1, &col), 0);
QCOMPARE(col, 0);
}
void PointStorageTest::testDimension()
{
PointStorage<int> storage;
QCOMPARE(storage.rows(), 0);
QCOMPARE(storage.columns(), 0);
storage.insert(1, 1, 27);
QCOMPARE(storage.rows(), 1);
QCOMPARE(storage.columns(), 1);
storage.insert(3, 1, 27);
QCOMPARE(storage.rows(), 1);
QCOMPARE(storage.columns(), 3);
storage.insert(3, 9, 27);
QCOMPARE(storage.rows(), 9);
QCOMPARE(storage.columns(), 3);
storage.insert(9, 9, 27);
QCOMPARE(storage.rows(), 9);
QCOMPARE(storage.columns(), 9);
storage.insert(10, 9, 27);
QCOMPARE(storage.rows(), 9);
QCOMPARE(storage.columns(), 10);
storage.insert(10, 10, 27);
QCOMPARE(storage.rows(), 10);
QCOMPARE(storage.columns(), 10);
}
void PointStorageTest::testSubStorage()
{
// #if 0
PointStorage<int> storage;
storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
storage.m_rows << 0 << 3 << 6 << 9 << 10;
storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
PointStorage<int> subStorage;
subStorage = storage.subStorage(Region(QRect(1, 1, 5, 5))); // all
// ( 1, 2, , , 3)
// ( 4, 5, 6, , )
// ( , 7, 8, , 9)
// ( , , ,10, )
// (11, , , ,12)
QVector<int> data = QVector<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12;
QVector<int> rows = QVector<int>() << 0 << 3 << 6 << 9 << 10;
QVector<int> cols = QVector<int>() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5;
QCOMPARE(subStorage.m_data, data);
QCOMPARE(subStorage.m_rows, rows);
QCOMPARE(subStorage.m_cols, cols);
subStorage = storage.subStorage(Region(QRect(1, 1, 3, 3))); // upper left
// ( 1, 2, )
// ( 4, 5, 6)
// ( , 7, 8)
data = QVector<int>() << 1 << 2 << 4 << 5 << 6 << 7 << 8;
rows = QVector<int>() << 0 << 2 << 5;
cols = QVector<int>() << 1 << 2 << 1 << 2 << 3 << 2 << 3;
QCOMPARE(subStorage.m_data, data);
QCOMPARE(subStorage.m_rows, rows);
QCOMPARE(subStorage.m_cols, cols);
subStorage = storage.subStorage(Region(QRect(4, 4, 5, 5))); // lower right
// ( , , , , )
// ( , , , , )
// ( , , , , )
// ( , , ,10, )
// ( , , , ,12)
data = QVector<int>() << 10 << 12;
rows = QVector<int>() << 0 << 0 << 0 << 0 << 1;
cols = QVector<int>() << 4 << 5;
QCOMPARE(subStorage.m_data, data);
QCOMPARE(subStorage.m_rows, rows);
QCOMPARE(subStorage.m_cols, cols);
// #endif
}
QTEST_MAIN(PointStorageTest)
diff --git a/sheets/tests/TestPointStorage.h b/sheets/tests/TestPointStorage.h
index 6e179c8222b..57eb2a18802 100644
--- a/sheets/tests/TestPointStorage.h
+++ b/sheets/tests/TestPointStorage.h
@@ -1,64 +1,64 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_POINT_STORAGE_TEST
#define CALLIGRA_SHEETS_POINT_STORAGE_TEST
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class PointStorageTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testInsertion();
void testLookup();
void testDeletion();
void testInsertColumns();
void testDeleteColumns();
void testInsertRows();
void testDeleteRows();
void testShiftLeft();
void testShiftRight();
void testShiftUp();
void testShiftDown();
void testShiftDownUp();
void testFirstInColumn();
void testFirstInRow();
void testLastInColumn();
void testLastInRow();
void testNextInColumn();
void testNextInRow();
void testPrevInColumn();
void testPrevInRow();
void testIteration();
void testColumnIteration();
void testRowIteration();
void testDimension();
void testSubStorage();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_POINT_STORAGE_TEST
diff --git a/sheets/tests/TestRTree.cpp b/sheets/tests/TestRTree.cpp
index 8e117cfc67e..c6cb4526d81 100644
--- a/sheets/tests/TestRTree.cpp
+++ b/sheets/tests/TestRTree.cpp
@@ -1,335 +1,338 @@
#include <QSharedData>
#include "RTree.h"
#include "TestRTree.h"
+#include <QTest>
+
using namespace Calligra::Sheets;
class TestClass : public QSharedData
{
public:
TestClass() : member() {}
TestClass(const QString& m) : member(m) {}
virtual ~TestClass() {}
virtual int type() const {
return 0;
}
bool operator<(const TestClass& other) const {
return member < other.member;
}
bool operator==(const TestClass& other) const {
return member == other.member;
}
QString member;
};
class SharedTestClass
{
public:
SharedTestClass() : d(new TestClass()) {}
SharedTestClass(TestClass* subStyle) : d(subStyle) {}
inline const TestClass *operator->() const {
return d.data();
}
bool operator<(const SharedTestClass& o) const {
return d->operator<(*o.d.data());
}
bool operator==(const SharedTestClass& o) const {
return d->operator==(*o.d.data());
}
private:
QSharedDataPointer<TestClass> d;
};
class DerivedClass : public TestClass
{
public:
DerivedClass() : TestClass() {}
DerivedClass(const QString& s) : TestClass(s) {}
virtual int type() const {
return 1;
}
};
void TestRTree::testIntersectingPairs()
{
RTree<SharedTestClass> tree;
tree.insert(QRect(1, 1, 1, 1), new DerivedClass(QString("foo")));
QList< QPair<QRectF, SharedTestClass> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QVERIFY(pairs.count() == 1);
QCOMPARE(pairs[0].first, QRectF(1, 1, 1, 1));
QCOMPARE(pairs[0].second->member, QString("foo"));
QCOMPARE(pairs[0].second->type(), 1);
}
void TestRTree::testInsertShiftRight()
{
RTree<SharedTestClass> tree;
tree.insert(QRect(2, 2, 2, 1), new DerivedClass(QString("foo")));
tree.insertShiftRight(QRect(2, 1, 3, 4));
QList< QPair<QRectF, SharedTestClass> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 3);
QCOMPARE(pairs[0].first, QRectF(2, 2, 2, 1));
QCOMPARE(pairs[0].second->member, QString("foo"));
QCOMPARE(pairs[1].first, QRectF(2, 1, KS_colMax - 1, 4));
QCOMPARE(pairs[1].second->member, QString(""));
QCOMPARE(pairs[2].first, QRectF(5, 2, 2, 1));
QCOMPARE(pairs[2].second->member, QString("foo"));
}
void TestRTree::testInsertShiftDown()
{
RTree<SharedTestClass> tree;
tree.insert(QRect(2, 2, 1, 2), new DerivedClass(QString("foo")));
tree.insertShiftDown(QRect(2, 1, 4, 3));
QList< QPair<QRectF, SharedTestClass> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 3);
QCOMPARE(pairs[0].first, QRectF(2, 2, 1, 2));
QCOMPARE(pairs[0].second->member, QString("foo"));
QCOMPARE(pairs[1].first, QRectF(2, 1, 4, KS_rowMax));
QCOMPARE(pairs[1].second->member, QString(""));
QCOMPARE(pairs[2].first, QRectF(2, 5, 1, 2));
QCOMPARE(pairs[2].second->member, QString("foo"));
}
void TestRTree::testRemoveShiftLeft()
{
RTree<SharedTestClass> tree;
tree.insert(QRect(5, 2, 2, 1), new DerivedClass(QString("foo")));
tree.removeShiftLeft(QRect(2, 1, 3, 4));
QList< QPair<QRectF, SharedTestClass> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 3);
QCOMPARE(pairs[0].first, QRectF(5, 2, 2, 1));
QCOMPARE(pairs[0].second->member, QString("foo"));
QCOMPARE(pairs[1].first, QRectF(2, 1, KS_colMax - 1, 4));
QCOMPARE(pairs[1].second->member, QString(""));
QCOMPARE(pairs[2].first, QRectF(2, 2, 2, 1));
QCOMPARE(pairs[2].second->member, QString("foo"));
}
void TestRTree::testRemoveShiftUp()
{
RTree<SharedTestClass> tree;
tree.insert(QRect(2, 5, 1, 2), new DerivedClass(QString("foo")));
tree.removeShiftUp(QRect(2, 1, 4, 3));
QList< QPair<QRectF, SharedTestClass> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 3);
QCOMPARE(pairs[0].first, QRectF(2, 5, 1, 2));
QCOMPARE(pairs[0].second->member, QString("foo"));
QCOMPARE(pairs[1].first, QRectF(2, 1, 4, KS_rowMax));
QCOMPARE(pairs[1].second->member, QString(""));
QCOMPARE(pairs[2].first, QRectF(2, 2, 1, 2));
QCOMPARE(pairs[2].second->member, QString("foo"));
}
void TestRTree::testInsertColumns()
{
// RTree::InsertMode = RTree::CopyPrevious
RTree<QString> tree;
tree.insert(QRect(1, 1, 2, 1), QString("1"));
tree.insert(QRect(1, 2, 3, 1), QString("2"));
tree.insert(QRect(2, 3, 4, 1), QString("3"));
tree.insert(QRect(2, 4, 5, 1), QString("4"));
tree.insert(QRect(3, 5, 3, 1), QString("5"));
tree.insert(QRect(3, 6, 4, 1), QString("6"));
tree.insert(QRect(4, 7, 2, 1), QString("7"));
tree.insert(QRect(4, 8, 3, 1), QString("8"));
tree.insert(QRect(6, 9, 3, 1), QString("9"));
QList< QPair<QRectF, QString> > undo = tree.insertColumns(3, 3);
QList< QPair<QRectF, QString> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 9);
QCOMPARE(pairs[0].first, QRectF(1, 1, 5, 1));
QCOMPARE(pairs[0].second, QString("1"));
QCOMPARE(pairs[1].first, QRectF(1, 2, 6, 1));
QCOMPARE(pairs[1].second, QString("2"));
QCOMPARE(pairs[2].first, QRectF(2, 3, 7, 1));
QCOMPARE(pairs[2].second, QString("3"));
QCOMPARE(pairs[3].first, QRectF(2, 4, 8, 1));
QCOMPARE(pairs[3].second, QString("4"));
QCOMPARE(pairs[4].first, QRectF(6, 5, 3, 1));
QCOMPARE(pairs[4].second, QString("5"));
QCOMPARE(pairs[5].first, QRectF(6, 6, 4, 1));
QCOMPARE(pairs[5].second, QString("6"));
QCOMPARE(pairs[6].first, QRectF(7, 7, 2, 1));
QCOMPARE(pairs[6].second, QString("7"));
QCOMPARE(pairs[7].first, QRectF(7, 8, 3, 1));
QCOMPARE(pairs[7].second, QString("8"));
QCOMPARE(pairs[8].first, QRectF(9, 9, 3, 1));
QCOMPARE(pairs[8].second, QString("9"));
QCOMPARE(undo.count(), 0);
#if 0
// RTree::InsertMode = RTree::CopyCurrent
tree.clear();
tree.insert(QRect(1, 1, 2, 1), QString("1"));
tree.insert(QRect(1, 2, 3, 1), QString("2"));
undo = tree.insertColumns(3, 3, RTree<QString>::CopyCurrent);
pairs = tree.intersectingPairs(QRect(1, 1, 10, 10));
QCOMPARE(pairs.count(), 2);
QCOMPARE(pairs[0].first, QRectF(1, 1, 2, 1));
QCOMPARE(pairs[1].first, QRectF(1, 2, 6, 1));
QCOMPARE(undo.count(), 0);
// RTree::InsertMode = RTree::CopyNone
tree.clear();
tree.insert(QRect(1, 1, 2, 1), QString("1"));
tree.insert(QRect(1, 2, 3, 1), QString("2"));
undo = tree.insertColumns(3, 3, RTree<QString>::CopyNone);
pairs = tree.intersectingPairs(QRect(1, 1, 10, 10));
QCOMPARE(pairs.count(), 3);
QCOMPARE(pairs[0].first, QRectF(1, 1, 2, 1));
QCOMPARE(pairs[1].first, QRectF(1, 2, 2, 1));
QCOMPARE(pairs[2].first, QRectF(6, 2, 1, 1));
QCOMPARE(undo.count(), 0);
#endif
}
void TestRTree::testInsertRows()
{
// RTree::InsertMode = RTree::CopyPrevious
RTree<QString> tree;
tree.insert(QRect(1, 1, 1, 2), QString("1"));
tree.insert(QRect(2, 1, 1, 3), QString("2"));
tree.insert(QRect(3, 2, 1, 4), QString("3"));
tree.insert(QRect(4, 2, 1, 5), QString("4"));
tree.insert(QRect(5, 3, 1, 3), QString("5"));
tree.insert(QRect(6, 3, 1, 4), QString("6"));
tree.insert(QRect(7, 4, 1, 2), QString("7"));
tree.insert(QRect(8, 4, 1, 3), QString("8"));
tree.insert(QRect(9, 6, 1, 3), QString("9"));
QList< QPair<QRectF, QString> > undo = tree.insertRows(3, 3);
QList< QPair<QRectF, QString> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 9);
QCOMPARE(pairs[0].first, QRectF(1, 1, 1, 2));
QCOMPARE(pairs[0].second, QString("1"));
QCOMPARE(pairs[1].first, QRectF(2, 1, 1, 6));
QCOMPARE(pairs[1].second, QString("2"));
QCOMPARE(pairs[2].first, QRectF(3, 2, 1, 7));
QCOMPARE(pairs[2].second, QString("3"));
QCOMPARE(pairs[3].first, QRectF(4, 2, 1, 8));
QCOMPARE(pairs[3].second, QString("4"));
QCOMPARE(pairs[4].first, QRectF(5, 6, 1, 3));
QCOMPARE(pairs[4].second, QString("5"));
QCOMPARE(pairs[5].first, QRectF(6, 6, 1, 4));
QCOMPARE(pairs[5].second, QString("6"));
QCOMPARE(pairs[6].first, QRectF(7, 7, 1, 2));
QCOMPARE(pairs[6].second, QString("7"));
QCOMPARE(pairs[7].first, QRectF(8, 7, 1, 3));
QCOMPARE(pairs[7].second, QString("8"));
QCOMPARE(pairs[8].first, QRectF(9, 9, 1, 3));
QCOMPARE(pairs[8].second, QString("9"));
QCOMPARE(undo.count(), 0);
#if 0
// RTree::InsertMode = RTree::CopyCurrent
tree.clear();
tree.insert(QRect(1, 1, 1, 2), QString("1"));
tree.insert(QRect(2, 1, 1, 3), QString("2"));
undo = tree.insertColumns(3, 3, RTree<QString>::CopyCurrent);
pairs = tree.intersectingPairs(QRect(1, 1, 10, 10));
QCOMPARE(pairs.count(), 2);
QCOMPARE(pairs[0].first, QRectF(1, 1, 1, 2));
QCOMPARE(pairs[1].first, QRectF(2, 1, 1, 6));
QCOMPARE(undo.count(), 0);
#endif
RTree<bool> boolTree;
boolTree.insert(QRect(1, 2, 2, 1), true);
boolTree.insert(QRect(1, 3, 1, 2), true);
boolTree.insertRows(6, 1);
QList< QPair<QRectF, bool> > boolPairs = boolTree.intersectingPairs(QRect(1, 2, 1, 1)).values();
QCOMPARE(boolPairs.count(), 1);
QCOMPARE(boolPairs[0].first, QRectF(1, 2, 2, 1));
QCOMPARE(boolPairs[0].second, true);
boolPairs = boolTree.intersectingPairs(QRect(1, 3, 1, 1)).values();
QCOMPARE(boolPairs.count(), 1);
QCOMPARE(boolPairs[0].first, QRectF(1, 3, 1, 2));
QCOMPARE(boolPairs[0].second, true);
}
void TestRTree::testRemoveColumns()
{
RTree<QString> tree;
tree.insert(QRect(1, 1, 2, 1), QString("1"));
tree.insert(QRect(1, 2, 3, 1), QString("2"));
tree.insert(QRect(2, 3, 4, 1), QString("3"));
tree.insert(QRect(2, 4, 5, 1), QString("4"));
tree.insert(QRect(3, 5, 3, 1), QString("5"));
tree.insert(QRect(3, 6, 4, 1), QString("6"));
tree.insert(QRect(4, 7, 2, 1), QString("7"));
tree.insert(QRect(4, 8, 3, 1), QString("8"));
tree.insert(QRect(6, 9, 3, 1), QString("9"));
QList< QPair<QRectF, QString> > undo = tree.removeColumns(3, 3);
QList< QPair<QRectF, QString> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 7);
QCOMPARE(pairs[0].first, QRectF(1, 1, 2, 1));
QCOMPARE(pairs[0].second, QString("1"));
QCOMPARE(pairs[1].first, QRectF(1, 2, 2, 1));
QCOMPARE(pairs[1].second, QString("2"));
QCOMPARE(pairs[2].first, QRectF(2, 3, 1, 1));
QCOMPARE(pairs[2].second, QString("3"));
QCOMPARE(pairs[3].first, QRectF(2, 4, 2, 1));
QCOMPARE(pairs[3].second, QString("4"));
QCOMPARE(pairs[4].first, QRectF(3, 6, 1, 1));
QCOMPARE(pairs[4].second, QString("6"));
QCOMPARE(pairs[5].first, QRectF(3, 8, 1, 1));
QCOMPARE(pairs[5].second, QString("8"));
QCOMPARE(pairs[6].first, QRectF(3, 9, 3, 1));
QCOMPARE(pairs[6].second, QString("9"));
QCOMPARE(undo.count(), 2);
QCOMPARE(undo[0].first.toRect(), QRect(3, 5, 3, 1));
QCOMPARE(undo[0].second, QString("5"));
QCOMPARE(undo[1].first.toRect(), QRect(4, 7, 2, 1));
QCOMPARE(undo[1].second, QString("7"));
}
void TestRTree::testRemoveRows()
{
RTree<QString> tree;
tree.insert(QRect(1, 1, 1, 2), QString("1"));
tree.insert(QRect(2, 1, 1, 3), QString("2"));
tree.insert(QRect(3, 2, 1, 4), QString("3"));
tree.insert(QRect(4, 2, 1, 5), QString("4"));
tree.insert(QRect(5, 3, 1, 3), QString("5"));
tree.insert(QRect(6, 3, 1, 4), QString("6"));
tree.insert(QRect(7, 4, 1, 2), QString("7"));
tree.insert(QRect(8, 4, 1, 3), QString("8"));
tree.insert(QRect(9, 6, 1, 3), QString("9"));
QList< QPair<QRectF, QString> > undo = tree.removeRows(3, 3);
QList< QPair<QRectF, QString> > pairs = tree.intersectingPairs(QRect(1, 1, 10, 10)).values();
QCOMPARE(pairs.count(), 7);
QCOMPARE(pairs[0].first, QRectF(1, 1, 1, 2));
QCOMPARE(pairs[0].second, QString("1"));
QCOMPARE(pairs[1].first, QRectF(2, 1, 1, 2));
QCOMPARE(pairs[1].second, QString("2"));
QCOMPARE(pairs[2].first, QRectF(3, 2, 1, 1));
QCOMPARE(pairs[2].second, QString("3"));
QCOMPARE(pairs[3].first, QRectF(4, 2, 1, 2));
QCOMPARE(pairs[3].second, QString("4"));
QCOMPARE(pairs[4].first, QRectF(6, 3, 1, 1));
QCOMPARE(pairs[4].second, QString("6"));
QCOMPARE(pairs[5].first, QRectF(8, 3, 1, 1));
QCOMPARE(pairs[5].second, QString("8"));
QCOMPARE(pairs[6].first, QRectF(9, 3, 1, 3));
QCOMPARE(pairs[6].second, QString("9"));
QCOMPARE(undo.count(), 2);
QCOMPARE(undo[0].first.toRect(), QRect(5, 3, 1, 3));
QCOMPARE(undo[0].second, QString("5"));
QCOMPARE(undo[1].first.toRect(), QRect(7, 4, 1, 2));
QCOMPARE(undo[1].second, QString("7"));
}
void TestRTree::testPrimitive()
{
RTree<bool> tree;
tree.insert(QRect(2, 5, 1, 2), true);
QCOMPARE(tree.contains(QPoint(2, 2)).isEmpty(), true);
QCOMPARE(tree.contains(QPoint(2, 5)).first(), true);
QCOMPARE(tree.contains(QPoint(3, 5)).isEmpty(), true);
QCOMPARE(tree.contains(QPoint(2, 6)).first(), true);
const QList< QPair<QRectF, bool> > pairs = tree.intersectingPairs(QRect(2, 5, 1, 2)).values();
QCOMPARE(pairs.count(), 1);
QCOMPARE(pairs.first().first.toRect(), QRect(2, 5, 1, 2));
QCOMPARE(pairs.first().second, true);
}
+
QTEST_MAIN(TestRTree)
diff --git a/sheets/tests/TestRTree.h b/sheets/tests/TestRTree.h
index ba53c660abf..8002067b6b0 100644
--- a/sheets/tests/TestRTree.h
+++ b/sheets/tests/TestRTree.h
@@ -1,30 +1,30 @@
#ifndef CALLIGRA_SHEETS_TEST_RTREE
#define CALLIGRA_SHEETS_TEST_RTREE
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class TestRTree: public QObject
{
Q_OBJECT
private Q_SLOTS:
void testIntersectingPairs();
void testInsertShiftRight();
void testInsertShiftDown();
void testRemoveShiftLeft();
void testRemoveShiftUp();
void testInsertColumns();
void testInsertRows();
void testRemoveColumns();
void testRemoveRows();
void testPrimitive();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_RTREE
diff --git a/sheets/tests/TestRegion.cpp b/sheets/tests/TestRegion.cpp
index b83aa351852..88481c0d065 100644
--- a/sheets/tests/TestRegion.cpp
+++ b/sheets/tests/TestRegion.cpp
@@ -1,154 +1,154 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestRegion.h"
-#include "qtest_kde.h"
+#include <QTest>
#include "calligra_sheets_limits.h"
#include "Map.h"
#include "Region.h"
#include "Sheet.h"
using namespace Calligra::Sheets;
void TestRegion::initTestCase()
{
m_map = new Map(0 /* no Doc*/);
Sheet* sheet = m_map->addNewSheet();
sheet->setSheetName("Sheet1");
sheet = m_map->addNewSheet();
sheet->setSheetName("Sheet2");
sheet = m_map->addNewSheet();
sheet->setSheetName("Sheet3");
sheet = m_map->addNewSheet();
sheet->setSheetName("Sheet 4");
}
void TestRegion::testComparison()
{
Region region1;
Region region2;
region1 = Region("A1");
region2 = Region("A1");
QVERIFY(region1 == region2);
region1 = Region("A1:A5");
region2 = Region("A1:A5");
QVERIFY(region1 == region2);
region1 = Region("A1:A5;B4");
region2 = Region("A1:A5;B4");
QVERIFY(region1 == region2);
region2 = Region("A1");
QVERIFY(region1 != region2);
region2 = Region("A1:A5");
QVERIFY(region1 != region2);
}
void TestRegion::testFixation()
{
Region region;
region = Region("$A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!$A1"));
region = Region("A$1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A$1"));
region = Region("$A$1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!$A$1"));
region = Region("$A1:B4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!$A1:B4"));
region = Region("A$1:B4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A$1:B4"));
region = Region("$A$1:B4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!$A$1:B4"));
region = Region("A1:$B4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A1:$B4"));
region = Region("A1:B$4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A1:B$4"));
region = Region("A1:$B$4", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A1:$B$4"));
}
void TestRegion::testSheet()
{
Region region;
region = Region(QPoint(1, 1), m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(0));
region = Region("A1");
QCOMPARE(region.name(), QString("A1"));
QCOMPARE(region.firstSheet(), (Sheet*)0);
region = Region("A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet1!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(0));
region = Region("Sheet1!A1", m_map, m_map->sheet(1));
QCOMPARE(region.name(), QString("Sheet1!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(0));
region = Region("Sheet2!A1", m_map);
QCOMPARE(region.name(), QString("Sheet2!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(1));
region = Region("Sheet2!A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("Sheet2!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(1));
region = Region("Sheet 4!A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("'Sheet 4'!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(3));
region = Region("'Sheet 4'!A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("'Sheet 4'!A1"));
QCOMPARE(region.firstSheet(), m_map->sheet(3));
// Multiple quotas should be compreseed, use-case that
// was visible in the xls from bug 284325.
region = Region("'''Sheet 4'''!A1", m_map, m_map->sheet(0));
QCOMPARE(region.name(), QString("'Sheet 4'!A1"));
// invalid calls:
region = Region("!A1", m_map, m_map->sheet(0));
QVERIFY(region.isEmpty());
region = Region("Sheet99!A1", m_map, m_map->sheet(0));
QVERIFY(region.isEmpty());
}
void TestRegion::testExtrem()
{
Region region1 = Region(QPoint(-1, -1), m_map->sheet(0));
QVERIFY(region1.isEmpty());
QVERIFY(!region1.isValid());
Region region2 = Region("A1:A6553634523563453456356");
QVERIFY(region2.isValid());
Region region3 = Region(QRect(1,1,KS_colMax,KS_rowMax), m_map->sheet(0));
QVERIFY(region3.isValid());
Region region4 = Region(QRect(1,1,KS_colMax,KS_rowMax), m_map->sheet(0));
QVERIFY(region4.isValid());
Region region5 = Region(QRect(1,1,KS_colMax+12345,KS_rowMax+12345), m_map->sheet(0));
QVERIFY(region5.isValid());
QCOMPARE(region4.name(), region5.name());
Region region6 = Region(QPoint(KS_colMax, KS_rowMax), m_map->sheet(0));
QVERIFY(region6.isValid());
Region region7 = Region(QPoint(KS_colMax+22, KS_rowMax+22), m_map->sheet(0));
QVERIFY(region7.isValid());
QCOMPARE(region6.name(), region7.name());
}
void TestRegion::cleanupTestCase()
{
delete m_map;
}
-QTEST_KDEMAIN(TestRegion, GUI)
+QTEST_MAIN(TestRegion)
diff --git a/sheets/tests/TestRegion.h b/sheets/tests/TestRegion.h
index 789307813b1..6a6a19b856b 100644
--- a/sheets/tests/TestRegion.h
+++ b/sheets/tests/TestRegion.h
@@ -1,51 +1,50 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_REGION
#define CALLIGRA_SHEETS_TEST_REGION
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class Map;
class TestRegion : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testComparison();
void testFixation();
void testSheet();
void testExtrem();
void cleanupTestCase();
private:
Map* m_map;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_REGION
diff --git a/sheets/tests/TestRowFormatStorage.cpp b/sheets/tests/TestRowFormatStorage.cpp
index fddbc3b820c..433a0110eda 100644
--- a/sheets/tests/TestRowFormatStorage.cpp
+++ b/sheets/tests/TestRowFormatStorage.cpp
@@ -1,322 +1,322 @@
/* This file is part of the KDE project
Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestRowFormatStorage.h"
#include <QVector>
#include <QDebug>
-#include <qtest_kde.h>
+#include <QTest>
#include "../RowFormatStorage.h"
#include "../calligra_sheets_limits.h"
#include "../Map.h"
#include "../Sheet.h"
#include "../RowColumnFormat.h"
using namespace Calligra::Sheets;
void TestRowFormatStorage::initTestCase()
{
m_map = new Map();
m_sheet = m_map->addNewSheet();
}
void TestRowFormatStorage::cleanupTestCase()
{
delete m_map;
}
void TestRowFormatStorage::testRowHeight()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(10, 100, 42.0);
for (int row = 1; row < 10000; row++) {
QCOMPARE(s.rowHeight(row), row >= 10 && row <= 100 ? 42.0 : m_map->defaultRowFormat()->height());
}
int firstRow, lastRow;
s.rowHeight(5, &lastRow);
QCOMPARE(lastRow, 9);
s.rowHeight(5, 0, &firstRow);
QCOMPARE(firstRow, 1);
s.rowHeight(50, &lastRow, &firstRow);
QCOMPARE(firstRow, 10);
QCOMPARE(lastRow, 100);
s.rowHeight(150, &lastRow);
QCOMPARE(lastRow, KS_rowMax);
}
void TestRowFormatStorage::testHidden()
{
RowFormatStorage s(m_sheet);
s.setHidden(10, 20, true);
int first, last;
QVERIFY(s.isHidden(15, &last, &first));
QCOMPARE(first, 10);
QCOMPARE(last, 20);
QVERIFY(!s.isHidden(5, &last));
QCOMPARE(last, 9);
QVERIFY(!s.isHidden(25, 0, &first));
QCOMPARE(first, 21);
}
void TestRowFormatStorage::testFiltered()
{
RowFormatStorage s(m_sheet);
s.setFiltered(10, 20, true);
int first, last;
QVERIFY(s.isFiltered(15, &last, &first));
QCOMPARE(first, 10);
QCOMPARE(last, 20);
QVERIFY(!s.isFiltered(5, &last));
QCOMPARE(last, 9);
QVERIFY(!s.isFiltered(25, 0, &first));
QCOMPARE(first, 21);
}
void TestRowFormatStorage::testHiddenOrFiltered()
{
RowFormatStorage s(m_sheet);
s.setHidden(5, 10, true);
s.setHidden(20, 30, true);
s.setFiltered(2, 7, true);
s.setFiltered(10, 22, true);
for (int row = 1; row < 10000; row++) {
if (row < 2 || row > 30) {
QVERIFY(!s.isHiddenOrFiltered(row));
} else {
QVERIFY(s.isHiddenOrFiltered(row));
}
}
int first, last;
QVERIFY(!s.isHiddenOrFiltered(1, &last, &first));
QCOMPARE(first, 1);
QCOMPARE(last, 1);
QVERIFY(s.isHiddenOrFiltered(3, &last, &first));
QCOMPARE(first, 2);
QCOMPARE(last, 4);
QVERIFY(s.isHiddenOrFiltered(7, &last, &first));
QCOMPARE(first, 5);
QCOMPARE(last, 7);
}
void TestRowFormatStorage::testVisibleHeight()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(20, 100, 42.0);
s.setFiltered(10, 30, true);
s.setHidden(90, 110, true);
int first, last;
QCOMPARE(s.visibleHeight(25, &last, &first), 0.0);
QCOMPARE(first, 10);
QCOMPARE(last, 30);
QCOMPARE(s.visibleHeight(55, &last, &first), 42.0);
QCOMPARE(first, 31);
QCOMPARE(last, 89);
QCOMPARE(s.visibleHeight(97, &last, &first), 0.0);
QCOMPARE(first, 90);
QCOMPARE(last, 110);
}
void TestRowFormatStorage::testTotalRowHeight()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(1, KS_rowMax, 10.0);
s.setRowHeight(10, 19, 13.0);
s.setRowHeight(100, 199, 17.0);
QCOMPARE(s.totalRowHeight(10, 19), 130.0);
QCOMPARE(s.totalRowHeight(9, 10), 23.0);
QCOMPARE(s.totalRowHeight(1, 19), 220.0);
QCOMPARE(s.totalRowHeight(1, KS_rowMax), (KS_rowMax - 110) * 10.0 + 130.0 + 1700.0);
}
void TestRowFormatStorage::testTotalVisibleRowHeight()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(1, KS_rowMax, 10.0);
s.setRowHeight(10, 19, 13.0);
s.setRowHeight(100, 199, 17.0);
s.setHidden(5, 5, true);
s.setHidden(13, 13, true);
s.setFiltered(110, 110, true);
QCOMPARE(s.totalVisibleRowHeight(10, 19), 130.0 - 13.0);
QCOMPARE(s.totalVisibleRowHeight(1, 19), 220.0 - 23.0);
QCOMPARE(s.totalVisibleRowHeight(1, KS_rowMax), (KS_rowMax - 110) * 10.0 + 130.0 + 1700.0 - 40.0);
}
void TestRowFormatStorage::testRowForPosition()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(1, KS_rowMax, 10.0);
s.setRowHeight(10, 19, 13.0);
s.setHidden(5, 5, true);
qreal top;
QCOMPARE(s.rowForPosition(5.0), 1);
QCOMPARE(s.rowForPosition(15.0), 2);
QCOMPARE(s.rowForPosition(45.0), 6);
QCOMPARE(s.rowForPosition(55.0, &top), 7);
QCOMPARE(top, 50.0);
QCOMPARE(s.rowForPosition(95.0, &top), 11);
QCOMPARE(top, 93.0);
}
void TestRowFormatStorage::testPageBreak()
{
RowFormatStorage s(m_sheet);
s.setPageBreak(10, 20, true);
int first, last;
QVERIFY(s.hasPageBreak(15, &last, &first));
QCOMPARE(first, 10);
QCOMPARE(last, 20);
QVERIFY(!s.hasPageBreak(5, &last));
QCOMPARE(last, 9);
QVERIFY(!s.hasPageBreak(25, 0, &first));
QCOMPARE(first, 21);
}
void TestRowFormatStorage::testIsDefaultRow()
{
RowFormatStorage s(m_sheet);
s.setFiltered(10, 13, true);
s.setHidden(15, 18, true);
s.setPageBreak(21, 25, true);
s.setRowHeight(23, 30, 42.0);
int first, last;
QVERIFY(s.isDefaultRow(5, &last, &first));
QCOMPARE(first, 1);
QCOMPARE(last, 9);
QVERIFY(!s.isDefaultRow(13, &last, &first));
QCOMPARE(first, 10);
QCOMPARE(last, 13);
QVERIFY(s.isDefaultRow(14, &last, &first));
QCOMPARE(first, 14);
QCOMPARE(last, 14);
QVERIFY(!s.isDefaultRow(22, &last, &first));
QCOMPARE(first, 21);
QCOMPARE(last, 22);
QVERIFY(!s.isDefaultRow(24, &last, &first));
QCOMPARE(first, 23);
QCOMPARE(last, 25);
QVERIFY(!s.isDefaultRow(27, &last, &first));
QCOMPARE(first, 26);
QCOMPARE(last, 30);
}
void TestRowFormatStorage::testSetDefault()
{
RowFormatStorage s(m_sheet);
s.setFiltered(10, 13, true);
s.setHidden(15, 18, true);
s.setPageBreak(21, 25, true);
s.setRowHeight(23, 30, 42.0);
s.setDefault(5, 16);
s.setDefault(24, 28);
QVERIFY(s.isDefaultRow(5));
QVERIFY(s.isDefaultRow(16));
QVERIFY(!s.isDefaultRow(17));
QVERIFY(!s.isDefaultRow(23));
QVERIFY(s.isDefaultRow(24));
QVERIFY(s.isDefaultRow(28));
QVERIFY(!s.isDefaultRow(29));
}
void TestRowFormatStorage::testLastNonDefaultRow()
{
RowFormatStorage s(m_sheet);
s.setFiltered(10, 13, true);
QCOMPARE(s.lastNonDefaultRow(), 13);
s.setHidden(15, 18, true);
QCOMPARE(s.lastNonDefaultRow(), 18);
s.setPageBreak(23, 25, true);
QCOMPARE(s.lastNonDefaultRow(), 25);
s.setRowHeight(15, 30, 42.0);
QCOMPARE(s.lastNonDefaultRow(), 30);
}
void TestRowFormatStorage::testRowsAreEqual()
{
RowFormatStorage s(m_sheet);
s.setFiltered(10, 13, true);
s.setHidden(15, 18, true);
s.setPageBreak(21, 25, true);
s.setRowHeight(23, 30, 42.0);
QVERIFY(!s.rowsAreEqual(9, 10));
QVERIFY(s.rowsAreEqual(10, 12));
QVERIFY(!s.rowsAreEqual(3, 15));
QVERIFY(s.rowsAreEqual(16, 18));
QVERIFY(!s.rowsAreEqual(20, 21));
QVERIFY(s.rowsAreEqual(21, 22));
QVERIFY(!s.rowsAreEqual(22, 23));
QVERIFY(s.rowsAreEqual(23, 25));
QVERIFY(!s.rowsAreEqual(24, 26));
QVERIFY(s.rowsAreEqual(27, 28));
QVERIFY(!s.rowsAreEqual(30, 31));
QVERIFY(s.rowsAreEqual(5, 55));
}
void TestRowFormatStorage::testInsertRows()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(10, 100, 42.0);
s.insertRows(20, 5);
s.insertRows(7, 5);
s.insertRows(150, 5);
for (int row = 1; row < 10000; row++) {
QCOMPARE(s.rowHeight(row), row >= 15 && row <= 110 ? 42.0 : m_map->defaultRowFormat()->height());
}
}
void TestRowFormatStorage::testRemoveRows()
{
RowFormatStorage s(m_sheet);
s.setRowHeight(10, 100, 42.0);
s.removeRows(20, 5);
s.removeRows(7, 5);
s.removeRows(85, 10);
for (int row = 1; row < 10000; row++) {
QCOMPARE(s.rowHeight(row), row >= 7 && row < 85 ? 42.0 : m_map->defaultRowFormat()->height());
}
}
-QTEST_KDEMAIN(TestRowFormatStorage, GUI)
+QTEST_MAIN(TestRowFormatStorage)
diff --git a/sheets/tests/TestRowRepeatStorage.cpp b/sheets/tests/TestRowRepeatStorage.cpp
index 91e1ab9fe5c..ada2821f9f4 100644
--- a/sheets/tests/TestRowRepeatStorage.cpp
+++ b/sheets/tests/TestRowRepeatStorage.cpp
@@ -1,653 +1,653 @@
/* This file is part of the KDE project
Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestRowRepeatStorage.h"
#include <QVector>
-#include <qtest_kde.h>
+#include <QTest>
#include "../RowRepeatStorage.h"
#include "../calligra_sheets_limits.h"
using namespace Calligra::Sheets;
void TestRowRepeatStorage::testEmptyStorage()
{
RowRepeatStorage s;
for (int i = 1; i <= KS_rowMax; i++) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
void TestRowRepeatStorage::testSimpleSetRowRepeat()
{
// test simple non-overlapping ranges
RowRepeatStorage s;
s.setRowRepeat(10, 5);
for (int i = 1; i <= KS_rowMax; i++) {
if (i >= 10 && i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
s.setRowRepeat(44, 7);
for (int i = 1; i <= KS_rowMax; i++) {
if (i >= 10 && i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i >= 44 && i < 51) {
QCOMPARE(s.rowRepeat(i), 7);
QCOMPARE(s.firstIdenticalRow(i), 44);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
s.setRowRepeat(15, 4);
for (int i = 1; i <= KS_rowMax; i++) {
if (i >= 10 && i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i >= 15 && i < 19) {
QCOMPARE(s.rowRepeat(i), 4);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i >= 44 && i < 51) {
QCOMPARE(s.rowRepeat(i), 7);
QCOMPARE(s.firstIdenticalRow(i), 44);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testOverlappingRanges_data()
{
QTest::addColumn<int>("firststart");
QTest::addColumn<int>("firstcount");
QTest::addColumn<int>("secondstart");
QTest::addColumn<int>("secondcount");
QTest::newRow("non overlapping") << 5 << 5 << 10 << 5;
QTest::newRow("overlap end 1") << 5 << 5 << 9 << 5;
QTest::newRow("overlap end 2") << 5 << 5 << 8 << 5;
QTest::newRow("overlap start 1") << 10 << 5 << 6 << 5;
QTest::newRow("overlap start 2") << 10 << 5 << 7 << 5;
QTest::newRow("overlap middle") << 5 << 10 << 7 << 5;
QTest::newRow("fully cover") << 7 << 5 << 5 << 10;
}
void TestRowRepeatStorage::testOverlappingRanges()
{
RowRepeatStorage s;
QFETCH(int, firststart);
QFETCH(int, firstcount);
QFETCH(int, secondstart);
QFETCH(int, secondcount);
s.setRowRepeat(firststart, firstcount);
s.setRowRepeat(secondstart, secondcount);
for (int i = 1; i <= KS_rowMax; i++) {
if (i >= secondstart && i < secondstart + secondcount) {
QCOMPARE(s.rowRepeat(i), secondcount);
QCOMPARE(s.firstIdenticalRow(i), secondstart);
} else if (i >= firststart && i < firststart + firstcount) {
if (i < secondstart) {
QCOMPARE(s.rowRepeat(i), qMin(firstcount, secondstart - firststart));
QCOMPARE(s.firstIdenticalRow(i), firststart);
} else {
QCOMPARE(s.rowRepeat(i), qMin(firstcount, firststart + firstcount - secondstart - secondcount));
QCOMPARE(s.firstIdenticalRow(i), qMax(firststart, secondstart + secondcount));
}
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testComplexSetRowRepeat()
{
RowRepeatStorage s;
s.setRowRepeat(1, 1000);
s.setRowRepeat(5, 100);
s.setRowRepeat(10, 10);
s.setRowRepeat(90, 50);
s.setRowRepeat(15, 100);
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 5) {
QCOMPARE(s.rowRepeat(i), 4);
QCOMPARE(s.firstIdenticalRow(i), 1);
} else if (i < 10) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 115) {
QCOMPARE(s.rowRepeat(i), 100);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i < 140) {
QCOMPARE(s.rowRepeat(i), 25);
QCOMPARE(s.firstIdenticalRow(i), 115);
} else if (i <= 1000) {
QCOMPARE(s.rowRepeat(i), 861);
QCOMPARE(s.firstIdenticalRow(i), 140);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertRowsEmpty()
{
RowRepeatStorage s;
s.insertRows(10, 5);
for (int i = 1; i <= KS_rowMax; i++) {
if (i >= 10 && i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertRowsBetween()
{
RowRepeatStorage s;
s.setRowRepeat(5, 10);
s.setRowRepeat(40, 10);
s.insertRows(20, 10);
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 5 || i >= 60) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 20);
} else if (i < 50) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 60) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 50);
}
}
}
void TestRowRepeatStorage::testInsertRowsMiddle()
{
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.insertRows(20, 7);
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10 || i >= 37) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 27) {
QCOMPARE(s.rowRepeat(i), 7);
QCOMPARE(s.firstIdenticalRow(i), 20);
} else if (i < 37) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 27);
}
}
}
void TestRowRepeatStorage::testRemoveRowsEmpty()
{
RowRepeatStorage s;
s.removeRows(10, 20);
for (int i = 1; i <= KS_rowMax; i++) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
void TestRowRepeatStorage::testRemoveRowsBetween()
{
RowRepeatStorage s;
s.setRowRepeat(5, 10);
s.setRowRepeat(50, 20);
s.removeRows(25, 5);
for (int i = 0; i < KS_rowMax; i++) {
if (i < 5 || i >= 65) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 45) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 65) {
QCOMPARE(s.rowRepeat(i), 20);
QCOMPARE(s.firstIdenticalRow(i), 45);
}
}
}
void TestRowRepeatStorage::testRemoveRowsOverlap()
{
RowRepeatStorage s;
s.setRowRepeat(5, 15);
s.setRowRepeat(30, 10);
s.setRowRepeat(12, 4);
s.removeRows(10, 22);
for (int i = 0; i < KS_rowMax; i++) {
if (i < 5 || i >= 18) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 10) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 18) {
QCOMPARE(s.rowRepeat(i), 8);
QCOMPARE(s.firstIdenticalRow(i), 10);
}
}
}
void TestRowRepeatStorage::testInsertShiftDown1()
{
// entire rect inside one row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.insertShiftDown(QRect(5, 15, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 25) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 25);
} else if (i < 210) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 250) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 210);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertShiftDown2()
{
// rect overlapping the end of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.insertShiftDown(QRect(5, 25, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 25) {
QCOMPARE(s.rowRepeat(i), 15);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 25);
} else if (i < 210) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 250) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 210);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertShiftDown3()
{
// rect overlapping the start of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.insertShiftDown(QRect(5, 5, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 20);
} else if (i < 210) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 250) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 210);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertShiftDown4()
{
// rect overlapping the start and end of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(35, 30);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.insertShiftDown(QRect(5, 15, 10, 25));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 15);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i < 35) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 40) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 35);
} else if (i < 55) {
QCOMPARE(s.rowRepeat(i), 15);
QCOMPARE(s.firstIdenticalRow(i), 40);
} else if (i < 60) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 65) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 60);
} else if (i < 225) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 250) {
QCOMPARE(s.rowRepeat(i), 25);
QCOMPARE(s.firstIdenticalRow(i), 225);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testRemoveShiftUp1()
{
// entire rect inside one row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.removeShiftUp(QRect(5, 15, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i < 200) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 240) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 200);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testRemoveShiftUp2()
{
// rect overlapping the end of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.removeShiftUp(QRect(5, 25, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 25) {
QCOMPARE(s.rowRepeat(i), 15);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 200) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 240) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 200);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testRemoveShiftUp3()
{
// rect overlapping the start of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.removeShiftUp(QRect(5, 5, 10, 10));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 200) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 240) {
QCOMPARE(s.rowRepeat(i), 40);
QCOMPARE(s.firstIdenticalRow(i), 200);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testRemoveShiftUp4()
{
// rect overlapping the start and end of a row-repeat, with a smaller and a larger row repeat after it
RowRepeatStorage s;
s.setRowRepeat(10, 20);
s.setRowRepeat(35, 30);
s.setRowRepeat(100, 5);
s.setRowRepeat(200, 50);
s.removeShiftUp(QRect(5, 15, 10, 25));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 10) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 15);
QCOMPARE(s.firstIdenticalRow(i), 15);
} else if (i < 35) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 40) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 35);
} else if (i < 200) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 225) {
QCOMPARE(s.rowRepeat(i), 25);
QCOMPARE(s.firstIdenticalRow(i), 200);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testInsertShiftRight()
{
RowRepeatStorage s;
s.setRowRepeat(5, 10);
s.setRowRepeat(20, 10);
s.setRowRepeat(35, 10);
s.insertShiftRight(QRect(5, 10, 10, 30));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 5) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 10) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 20);
} else if (i < 35) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 40) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 35);
} else if (i < 45) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 40);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
void TestRowRepeatStorage::testRemoveShiftLeft()
{
RowRepeatStorage s;
s.setRowRepeat(5, 10);
s.setRowRepeat(20, 10);
s.setRowRepeat(35, 10);
s.removeShiftLeft(QRect(5, 10, 10, 30));
for (int i = 1; i <= KS_rowMax; i++) {
if (i < 5) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 10) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 5);
} else if (i < 15) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 10);
} else if (i < 20) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 30) {
QCOMPARE(s.rowRepeat(i), 10);
QCOMPARE(s.firstIdenticalRow(i), 20);
} else if (i < 35) {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
} else if (i < 40) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 35);
} else if (i < 45) {
QCOMPARE(s.rowRepeat(i), 5);
QCOMPARE(s.firstIdenticalRow(i), 40);
} else {
QCOMPARE(s.rowRepeat(i), 1);
QCOMPARE(s.firstIdenticalRow(i), i);
}
}
}
-QTEST_KDEMAIN(TestRowRepeatStorage, NoGUI)
+QTEST_GUILESS_MAIN(TestRowRepeatStorage)
diff --git a/sheets/tests/TestSelection.cpp b/sheets/tests/TestSelection.cpp
index 90c704c0493..a020f8d1ea6 100644
--- a/sheets/tests/TestSelection.cpp
+++ b/sheets/tests/TestSelection.cpp
@@ -1,463 +1,463 @@
/* This file is part of the KDE project
Copyright 2009 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestSelection.h"
-#include <qtest_kde.h>
+#include <QTest>
#include "part/Canvas.h"
#include "Map.h"
#include "ui/Selection.h"
#include "Sheet.h"
using namespace Calligra::Sheets;
void TestSelection::initialize()
{
Map map;
Sheet* sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet* sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// Clearing in normal selection mode, results in A1 as residuum.
selection.clear();
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("Sheet1!A1"));
selection.initialize(QPoint(2, 4), sheet2);
QCOMPARE(selection.name(), QString("Sheet2!B4"));
selection.initialize(QRect(3, 5, 2, 3));
QCOMPARE(selection.name(), QString("Sheet1!C5:D7"));
Region region("A1:A4", &map, sheet2);
selection.initialize(region);
QCOMPARE(selection.name(), QString("Sheet2!A1:A4"));
region = Region("Sheet1!A2:A4", &map, sheet2);
selection.initialize(region);
QCOMPARE(selection.name(), QString("Sheet1!A2:A4"));
// reference mode:
selection.startReferenceSelection();
region = Region("Sheet1!A2:A4;B3;C5", &map, sheet2);
selection.initialize(region);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
selection.setActiveSubRegion(1, 1);
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!B3"));
selection.initialize(QPoint(2, 2));
QCOMPARE(selection.name(), QString("A2:A4;B2;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("B2"));
selection.setActiveSubRegion(1, 2, 2);
QCOMPARE(selection.activeSubRegionName(), QString("B2;Sheet2!C5"));
selection.initialize(QPoint(3, 3), sheet2);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!C3"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!C3"));
selection.setActiveSubRegion(1, 0, 2);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.initialize(QPoint(4, 4), sheet2);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!D4;Sheet2!C3"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!D4"));
selection.setActiveSubRegion(1, 1, 2);
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!D4"));
selection.clearSubRegion();
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!C3"));
QCOMPARE(selection.activeSubRegionName(), QString());
// Two appendings.
selection.clear();
selection.setActiveSubRegion(0, 0, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.initialize(QPoint(1, 1), sheet1);
QCOMPARE(selection.activeSubRegionName(), QString("A1"));
selection.setActiveSubRegion(1, 0, 1);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.initialize(QPoint(2, 2), sheet1);
QCOMPARE(selection.name(), QString("A1;B2"));
QCOMPARE(selection.activeSubRegionName(), QString("B2"));
}
void TestSelection::update()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// Selection::update(const QPoint&) works always on the active sheet.
// normal mode:
// init with location
selection.initialize(QPoint(1, 6), sheet1);
QCOMPARE(selection.name(), QString("Sheet1!A6"));
// init with region
selection.initialize(Region("A3", &map, sheet1));
QCOMPARE(selection.name(), QString("Sheet1!A3"));
// init with location after init with region
selection.initialize(QPoint(1, 5), sheet1);
QCOMPARE(selection.name(), QString("Sheet1!A5"));
// TODO
// reference mode:
selection.startReferenceSelection();
Region region("Sheet1!A2:A4;B3;C5", &map, sheet2);
selection.initialize(region);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
// Prepend new range.
selection.setActiveSubRegion(0, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.update(QPoint(1, 1));
QCOMPARE(selection.name(), QString("A1;A2:A4;Sheet2!B3;Sheet2!C5"));
// Insert new range on active sheet.
selection.setActiveSubRegion(2, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.update(QPoint(1, 1));
QCOMPARE(selection.name(), QString("A1;A2:A4;A1;Sheet2!B3;Sheet2!C5"));
// Append new range.
selection.setActiveSubRegion(5, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.update(QPoint(1, 1));
QCOMPARE(selection.name(), QString("A1;A2:A4;A1;Sheet2!B3;Sheet2!C5;A1"));
// Update range.
selection.setActiveSubRegion(2, 2);
QCOMPARE(selection.activeSubRegionName(), QString("A1;Sheet2!B3"));
selection.update(QPoint(2, 2));
QCOMPARE(selection.name(), QString("A1;A2:A4;A1:B2;Sheet2!B3;Sheet2!C5;A1"));
// Try to update range on non-active sheet. Inserts location on active sheet.
selection.setActiveSubRegion(2, 2, 3);
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2;Sheet2!B3"));
selection.update(QPoint(2, 2));
QCOMPARE(selection.name(), QString("A1;A2:A4;A1:B2;Sheet2!B3;B2;Sheet2!C5;A1"));
}
void TestSelection::extend()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// normal mode:
// TODO
// reference mode:
selection.startReferenceSelection();
Region region("Sheet1!A2:A4;B3;C5", &map, sheet2);
selection.initialize(region);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
// Prepend new location.
selection.setActiveSubRegion(0, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.extend(QPoint(1, 1), sheet2);
QCOMPARE(selection.name(), QString("Sheet2!A1;A2:A4;Sheet2!B3;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!A1"));
// Try to prepend new location. Prepending needs length = 0.
selection.setActiveSubRegion(0, 2); // results in: 0, 2, 0
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!A1;A2:A4"));
selection.extend(QPoint(1, 1), sheet1);
QCOMPARE(selection.name(), QString("Sheet2!A1;A1;A2:A4;Sheet2!B3;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!A1;A1;A2:A4"));
// Prepend new range.
selection.setActiveSubRegion(0, 0);
QCOMPARE(selection.activeSubRegionName(), QString());
selection.extend(QRect(1, 1, 2, 2), sheet1);
QCOMPARE(selection.name(), QString("A1:B2;Sheet2!A1;A1;A2:A4;Sheet2!B3;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2"));
// Try to prepend new range. Prepending needs length = 0.
selection.setActiveSubRegion(0, 3); // results in: 0, 3, 0
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2;Sheet2!A1;A1"));
selection.extend(QRect(1, 2, 2, 2), sheet2);
QCOMPARE(selection.name(), QString("A1:B2;Sheet2!A2:B3;Sheet2!A1;A1;A2:A4;Sheet2!B3;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2;Sheet2!A2:B3;Sheet2!A1;A1"));
selection.clear();
QVERIFY(selection.isEmpty());
selection.initialize(region);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
// Append new location.
selection.setActiveSubRegion(1, 3, 3); // results in: 1, 2, 3
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!B3;Sheet2!C5"));
selection.extend(QPoint(1, 1), sheet2);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5;Sheet2!A1"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!B3;Sheet2!C5;Sheet2!A1"));
// Append new location.
selection.setActiveSubRegion(4, 1, 3); // results in: 4, 0, 4
QCOMPARE(selection.activeSubRegionName(), QString());
selection.extend(QPoint(1, 1), sheet1);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5;Sheet2!A1;A1"));
QCOMPARE(selection.activeSubRegionName(), QString("A1"));
// Append new range.
selection.setActiveSubRegion(5, 0); // results in: 5, 0, 5
QCOMPARE(selection.activeSubRegionName(), QString());
selection.extend(QRect(1, 1, 2, 2), sheet1);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5;Sheet2!A1;A1;A1:B2"));
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2"));
// Append new range.
selection.setActiveSubRegion(5, 1, 6);
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2"));
selection.extend(QRect(1, 2, 2, 2), sheet2);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5;Sheet2!A1;A1;A1:B2;Sheet2!A2:B3"));
QCOMPARE(selection.activeSubRegionName(), QString("A1:B2;Sheet2!A2:B3"));
selection.clear();
QVERIFY(selection.isEmpty());
selection.initialize(region);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
// Insert new location.
selection.setActiveSubRegion(0, 3, 1);
QCOMPARE(selection.activeSubRegionName(), QString("A2:A4;Sheet2!B3;Sheet2!C5"));
selection.extend(QPoint(1, 1), sheet2);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!A1;Sheet2!C5"));
QCOMPARE(selection.activeSubRegionName(), QString("A2:A4;Sheet2!B3;Sheet2!A1;Sheet2!C5"));
// Insert new range.
selection.setActiveSubRegion(1, 3, 3);
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!B3;Sheet2!A1;Sheet2!C5"));
selection.extend(QRect(1, 1, 2, 2), sheet1);
QCOMPARE(selection.name(), QString("A2:A4;Sheet2!B3;Sheet2!A1;Sheet2!C5;A1:B2"));
QCOMPARE(selection.activeSubRegionName(), QString("Sheet2!B3;Sheet2!A1;Sheet2!C5;A1:B2"));
}
void TestSelection::activeElement()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
QVERIFY(!selection.activeElement());
// normal mode:
//BEGIN Leave the active element unset.
selection.startReferenceSelection();
QVERIFY(!selection.activeElement());
selection.initialize(Region("A3:A4;B2;C4:D5", &map, sheet1));
QCOMPARE(selection.name(sheet1), QString("A3:A4;B2;C4:D5"));
QVERIFY(!selection.activeElement());
selection.extend(QRect(1, 1, 4, 4), sheet1);
QCOMPARE(selection.name(sheet1), QString("A3:A4;B2;C4:D5;A1:D4"));
QVERIFY(!selection.activeElement());
selection.update(QPoint(5, 5));
QCOMPARE(selection.name(sheet1), QString("A3:A4;B2;C4:D5;A1:E5"));
QVERIFY(!selection.activeElement());
selection.initialize(QPoint(5, 6), sheet1);
QCOMPARE(selection.name(sheet1), QString("E6"));
QVERIFY(!selection.activeElement());
selection.initialize(QRect(1, 1, 2, 1), sheet1);
QCOMPARE(selection.name(sheet1), QString("A1:B1"));
QVERIFY(!selection.activeElement());
selection.extend(QPoint(1, 3), sheet1);
QCOMPARE(selection.name(sheet1), QString("A1:B1;A3"));
QVERIFY(!selection.activeElement());
//END Leave the active element unset.
}
void TestSelection::referenceSelectionMode()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// reference mode:
selection.startReferenceSelection();
QVERIFY(selection.isEmpty());
selection.initialize(QPoint(2, 2), sheet2);
QCOMPARE(selection.name(), QString("Sheet2!B2"));
selection.extend(QPoint(1, 2));
QCOMPARE(selection.name(), QString("Sheet2!B2;A2"));
selection.setActiveSubRegion(1, 1, 1);
QCOMPARE(selection.activeSubRegionName(), QString("A2"));
selection.endReferenceSelection();
QCOMPARE(selection.name(), QString("A1"));
selection.initialize(QPoint(2, 2), sheet1);
QCOMPARE(selection.name(), QString("B2"));
// quit it again
selection.endReferenceSelection();
QCOMPARE(selection.name(), QString("B2"));
selection.initialize(QRect(2, 2, 2, 2));
QCOMPARE(selection.name(), QString("B2:C3"));
// start it again
selection.startReferenceSelection();
QVERIFY(selection.isEmpty());
selection.initialize(Region("A3", &map, sheet1));
QCOMPARE(selection.name(), QString("A3"));
// set the active sub-region beyond the last range
selection.setActiveSubRegion(1, 0, 1);
QCOMPARE(selection.activeSubRegionName(), QString());
// quit it again
selection.endReferenceSelection();
QCOMPARE(selection.name(), QString("B2:C3"));
}
void TestSelection::covering()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// normal mode:
// convered ranges get removed
selection.initialize(Region("A3:A4;B2;C4:D5", &map, sheet1));
QCOMPARE(selection.name(sheet1), QString("A3:A4;B2;C4:D5"));
selection.extend(QRect(1, 1, 4, 4), sheet1);
QCOMPARE(selection.name(sheet1), QString("C4:D5;A1:D4"));
selection.update(QPoint(5, 5));
QCOMPARE(selection.name(sheet1), QString("A1:E5"));
// reference mode:
// covered ranges get ignored
selection.startReferenceSelection();
selection.initialize(Region("A3:A4;B2;C4:D5", &map, sheet1));
QCOMPARE(selection.name(), QString("A3:A4;B2;C4:D5"));
selection.extend(QRect(1, 1, 4, 4), sheet1);
QCOMPARE(selection.name(), QString("A3:A4;B2;C4:D5;A1:D4"));
selection.update(QPoint(5, 5));
QCOMPARE(selection.name(sheet1), QString("A3:A4;B2;C4:D5;A1:E5"));
}
void TestSelection::splitting()
{
Map map;
Sheet *sheet1 = new Sheet(&map, "Sheet1");
map.addSheet(sheet1);
Sheet *sheet2 = new Sheet(&map, "Sheet2");
map.addSheet(sheet2);
Canvas canvas(0);
Selection selection(&canvas);
selection.setActiveSheet(sheet1);
QVERIFY(!selection.isEmpty());
QCOMPARE(selection.name(), QString("A1"));
// normal mode:
// ranges get split
selection.initialize(Region("A1:D5", &map, sheet1));
QCOMPARE(selection.name(sheet1), QString("A1:D5"));
selection.extend(QPoint(2, 2), sheet1);
QCOMPARE(selection.name(sheet1), QString("A3:D5;C2:D2;A2;A1:D1"));
// reference mode:
// ranges are not affected
selection.startReferenceSelection();
selection.initialize(Region("A1:D5", &map, sheet1));
QCOMPARE(selection.name(), QString("A1:D5"));
selection.extend(QPoint(2, 2), sheet1);
QCOMPARE(selection.name(), QString("A1:D5;B2"));
}
-QTEST_KDEMAIN(TestSelection, GUI)
+QTEST_MAIN(TestSelection)
diff --git a/sheets/tests/TestSelection.h b/sheets/tests/TestSelection.h
index 618ef05a525..ad1bdcb1f4d 100644
--- a/sheets/tests/TestSelection.h
+++ b/sheets/tests/TestSelection.h
@@ -1,46 +1,46 @@
/* This file is part of the KDE project
Copyright 2009 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_SELECTION
#define CALLIGRA_SHEETS_TEST_SELECTION
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class TestSelection : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initialize();
void update();
void extend();
void activeElement();
void referenceSelectionMode();
void covering();
void splitting();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_SELECTION
diff --git a/sheets/tests/TestSheet.cpp b/sheets/tests/TestSheet.cpp
index 1c02924c3d4..4d0bde7d63a 100644
--- a/sheets/tests/TestSheet.cpp
+++ b/sheets/tests/TestSheet.cpp
@@ -1,205 +1,205 @@
/* This file is part of the KDE project
Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestSheet.h"
#include <QPainter>
#include <KoViewConverter.h>
#include <KoShape.h>
#include <KoShapeSavingContext.h>
#include <KoXmlWriter.h>
#include <KoGenStyles.h>
#include <KoEmbeddedDocumentSaver.h>
#include <KoPart.h>
#include <part/Doc.h> // FIXME detach from part
#include <Map.h>
#include <Sheet.h>
#include <CellStorage.h>
#include <OdfSavingContext.h>
-#include <qtest_kde.h>
+#include <QTest>
using namespace Calligra::Sheets;
class MockShape : public KoShape
{
public:
MockShape() : KoShape() {}
void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &) {
Q_UNUSED(painter);
Q_UNUSED(converter);
}
virtual void saveOdf(KoShapeSavingContext &) const {}
virtual bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &) {
return true;
}
};
void SheetTest::init()
{
m_doc = new Doc(new MockPart);
m_doc->map()->addNewSheet();
m_sheet = m_doc->map()->sheet(0);
m_sheet->map()->setDefaultRowHeight(10.0);
m_sheet->map()->setDefaultColumnWidth(10.0);
}
void SheetTest::cleanup()
{
delete m_doc;
}
void SheetTest::testRemoveRows_data()
{
QTest::addColumn<int>("col");
QTest::addColumn<int>("row");
QTest::addColumn<QString>("formula");
QTest::addColumn<int>("rowToRemove");
QTest::addColumn<int>("numRows");
QTest::addColumn<QString>("result");
QTest::newRow("exact row") << 1 << 1 << "=C4" << 4 << 1 << "=#Dependency!";
QTest::newRow("earlier row") << 1 << 1 << "=C4" << 3 << 1 << "=C3";
QTest::newRow("later row") << 1 << 1 << "=C4" << 5 << 1 << "=C4";
QTest::newRow("range before start") << 1 << 1 << "=SUM(C4:C7)" << 3 << 1 << "=SUM(C3:C6)";
QTest::newRow("range start row") << 1 << 1 << "=SUM(C4:C7)" << 4 << 1 << "=SUM(C4:C6)";
QTest::newRow("range middle row") << 1 << 1 << "=SUM(C4:C7)" << 5 << 1 << "=SUM(C4:C6)";
QTest::newRow("range end row") << 1 << 1 << "=SUM(C4:C7)" << 7 << 1 << "=SUM(C4:C6)";
QTest::newRow("range after end") << 1 << 1 << "=SUM(C4:C7)" << 8 << 1 << "=SUM(C4:C7)";
QTest::newRow("entire range") << 1 << 1 << "=SUM(C4:C4)" << 4 << 1 << "=SUM(#Dependency!:#Dependency!)";
QTest::newRow("2d range before start") << 1 << 1 << "=SUM(C4:E7)" << 3 << 1 << "=SUM(C3:E6)";
QTest::newRow("2d range start row") << 1 << 1 << "=SUM(C4:E7)" << 4 << 1 << "=SUM(C4:E6)";
QTest::newRow("2d range middle row") << 1 << 1 << "=SUM(C4:E7)" << 5 << 1 << "=SUM(C4:E6)";
QTest::newRow("2d range end row") << 1 << 1 << "=SUM(C4:E7)" << 7 << 1 << "=SUM(C4:E6)";
QTest::newRow("2d range after end") << 1 << 1 << "=SUM(C4:E7)" << 8 << 1 << "=SUM(C4:E7)";
QTest::newRow("refer to last deleted row") << 1 << 1 << "=A4" << 2 << 3 << "=#Dependency!";
QTest::newRow("refer to first not deleted row") << 1 << 1 << "=A4" << 2 << 2 << "=A2";
// Bug 313056
QTest::newRow("bug313056_1") << 4 << 5 << "=A1" << 3 << 2 << "=A1";
QTest::newRow("bug313056_2") << 2 << 32 << "=E9" << 5 << 26 << "=#Dependency!";
}
void SheetTest::testRemoveRows()
{
QFETCH(int, col);
QFETCH(int, row);
QFETCH(QString, formula);
QFETCH(int, rowToRemove);
QFETCH(int, numRows);
QFETCH(QString, result);
Cell cell(m_sheet, col, row);
cell.setUserInput(formula);
m_sheet->removeRows(rowToRemove, numRows);
QCOMPARE(cell.userInput(), result);
}
void SheetTest::testRemoveColumns_data()
{
QTest::addColumn<QString>("formula");
QTest::addColumn<int>("columnToRemove");
QTest::addColumn<QString>("result");
QTest::newRow("exact col") << "=C4" << 3 << "=#Dependency!";
QTest::newRow("earlier col") << "=C4" << 2 << "=B4";
QTest::newRow("later col") << "=C4" << 4 << "=C4";
QTest::newRow("range before start") << "=SUM(C4:E4)" << 2 << "=SUM(B4:D4)";
QTest::newRow("range start row") << "=SUM(C4:E4)" << 3 << "=SUM(C4:D4)";
QTest::newRow("range middle row") << "=SUM(C4:E4)" << 4 << "=SUM(C4:D4)";
QTest::newRow("range end row") << "=SUM(C4:E4)" << 5 << "=SUM(C4:D4)";
QTest::newRow("range after end") << "=SUM(C4:E4)" << 6 << "=SUM(C4:E4)";
QTest::newRow("entire range") << "=SUM(C4:C4)" << 3 << "=SUM(#Dependency!:#Dependency!)";
QTest::newRow("2d range before start") << "=SUM(C4:E7)" << 2 << "=SUM(B4:D7)";
QTest::newRow("2d range start row") << "=SUM(C4:E7)" << 3 << "=SUM(C4:D7)";
QTest::newRow("2d range middle row") << "=SUM(C4:E7)" << 4 << "=SUM(C4:D7)";
QTest::newRow("2d range end row") << "=SUM(C4:E7)" << 5 << "=SUM(C4:D7)";
QTest::newRow("2d range after end") << "=SUM(C4:E7)" << 6 << "=SUM(C4:E7)";
}
void SheetTest::testRemoveColumns()
{
QFETCH(QString, formula);
QFETCH(int, columnToRemove);
QFETCH(QString, result);
Cell cell(m_sheet, 1, 1);
cell.setUserInput(formula);
m_sheet->removeColumns(columnToRemove, 1);
QCOMPARE(cell.userInput(), result);
}
void SheetTest::testDocumentToCellCoordinates_data()
{
QTest::addColumn<QRectF>("area");
QTest::addColumn<QRect>("result");
QTest::newRow("simple") << QRectF(5, 5, 10, 10) << QRect(1, 1, 2, 2);
QTest::newRow("bigger") << QRectF(5, 5, 200, 100) << QRect(1, 1, 21, 11);
QTest::newRow("wide") << QRectF(5, 5, 300000, 10) << QRect(1, 1, 30001, 2);
QTest::newRow("tall") << QRectF(5, 5, 10, 300000) << QRect(1, 1, 2, 30001);
QTest::newRow("very tall") << QRectF(5, 5, 10, 10000000) << QRect(1, 1, 2, 1000001);
}
void SheetTest::testDocumentToCellCoordinates()
{
QFETCH(QRectF, area);
QFETCH(QRect, result);
QCOMPARE(m_sheet->documentToCellCoordinates(area), result);
}
// test if embedded objects are propare taken into account (tests for bug 287997)
void SheetTest::testCompareRows()
{
CellStorage* storage = m_sheet->cellStorage();
storage->insertRows(1, 15);
QBuffer buf;
buf.open(QIODevice::ReadWrite);
KoXmlWriter xmlWriter(&buf);
KoGenStyles mainStyles;
KoEmbeddedDocumentSaver embeddedSaver;
KoShapeSavingContext shapeContext(xmlWriter, mainStyles, embeddedSaver);
OdfSavingContext tableContext(shapeContext);
MockShape shape;
tableContext.insertCellAnchoredShape(m_sheet, 20, 5, &shape);
QCOMPARE(m_sheet->compareRows(12,19,1024,tableContext), true);
QCOMPARE(m_sheet->compareRows(12,20,1024,tableContext), false);
QCOMPARE(m_sheet->compareRows(12,21,1024,tableContext), true);
storage->insertRows(1, 15);
QCOMPARE(m_sheet->compareRows(12,19,1024,tableContext), true);
QCOMPARE(m_sheet->compareRows(12,20,1024,tableContext), false);
QCOMPARE(m_sheet->compareRows(12,21,1024,tableContext), true);
}
-QTEST_KDEMAIN(SheetTest, GUI)
+QTEST_MAIN(SheetTest)
diff --git a/sheets/tests/TestSheet.h b/sheets/tests/TestSheet.h
index 672bf0c9346..688df06cffe 100644
--- a/sheets/tests/TestSheet.h
+++ b/sheets/tests/TestSheet.h
@@ -1,57 +1,57 @@
/* This file is part of the KDE project
Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_SHEET_TEST
#define CALLIGRA_SHEETS_SHEET_TEST
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class Doc;
class Sheet;
class SheetTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();
void testRemoveRows_data();
void testRemoveRows();
void testRemoveColumns_data();
void testRemoveColumns();
void testDocumentToCellCoordinates_data();
void testDocumentToCellCoordinates();
void testCompareRows();
private:
Sheet* m_sheet;
Doc* m_doc;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_SHEET_TEST
diff --git a/sheets/tests/TestSort.cpp b/sheets/tests/TestSort.cpp
index 312c4c7072c..b3f1e35169d 100644
--- a/sheets/tests/TestSort.cpp
+++ b/sheets/tests/TestSort.cpp
@@ -1,141 +1,141 @@
/* This file is part of the KDE project
Copyright 2011 Juan Aquino <utcl95@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestSort.h"
-#include "qtest_kde.h"
+#include <QTest>
#include <KoCanvasBase.h>
#include "CellStorage.h"
#include "Map.h"
#include "Region.h"
#include "Sheet.h"
#include "ui/Selection.h"
#include "../commands/SortManipulator.h"
using namespace Calligra::Sheets;
void TestSort::AscendingOrder()
{
Map map;
Sheet* sheet = new Sheet(&map, "Sheet1");
map.addSheet(sheet);
Value cellvalue;
KoCanvasBase* canvas = 0;
Selection* selection = new Selection(canvas);
selection->setActiveSheet(sheet);
CellStorage* storage = sheet->cellStorage();
// Data to sort...
// Ascending
// A1 3
// A2 Empty
// A3 1
// more...
// Ascending
storage->setValue(1,1, Value(3));
storage->setValue(1,2, Value());
storage->setValue(1,3, Value(1));
// Selection
selection->clear();
selection->initialize(QRect(1,1,1,3), sheet);
QCOMPARE(selection->name(), QString("Sheet1!A1:A3"));
// Sort Manipulator
SortManipulator *const command = new SortManipulator();
command->setRegisterUndo(0);
command->setSheet(sheet);
// Parameters.
command->setSortRows(Qt::Vertical);
command->setSkipFirst(false);
command->setCopyFormat(false);
command->addCriterion(0, Qt::AscendingOrder, Qt::CaseInsensitive);
command->add(selection->lastRange());
QCOMPARE(selection->lastRange(), QRect(1,1,1,3));
// Execute sort
command->execute(selection->canvas());
QCOMPARE(storage->value(1,1),Value(1));
QCOMPARE(storage->value(1,2),Value(3));
QCOMPARE(storage->value(1,3),Value());
}
void TestSort::DescendingOrder()
{
Map map;
Sheet* sheet = new Sheet(&map, "Sheet1");
map.addSheet(sheet);
Value cellvalue;
KoCanvasBase* canvas = 0;
Selection* selection = new Selection(canvas);
selection->setActiveSheet(sheet);
CellStorage* storage = sheet->cellStorage();
// Data to sort...
// Descending
// B1 1
// B2 Empty
// B3 3
// Descending
storage->setValue(2,1, Value(1));
storage->setValue(2,2, Value());
storage->setValue(2,3, Value(3));
// Selection
selection->clear();
selection->initialize(QRect(2,1,1,3), sheet);
QCOMPARE(selection->name(), QString("Sheet1!B1:B3"));
// Sort Manipulator
SortManipulator *const command = new SortManipulator();
command->setRegisterUndo(0);
command->setSheet(sheet);
// Parameters.
command->setSortRows(Qt::Vertical);
command->setSkipFirst(false);
command->setCopyFormat(false);
command->addCriterion(0, Qt::DescendingOrder, Qt::CaseInsensitive);
command->add(selection->lastRange());
QCOMPARE(selection->lastRange(), QRect(2,1,1,3));
// Execute sort
command->execute(selection->canvas());
QCOMPARE(storage->value(2,1),Value(3));
QCOMPARE(storage->value(2,2),Value(1));
QCOMPARE(storage->value(2,3),Value());
}
-QTEST_KDEMAIN(TestSort, GUI)
+QTEST_MAIN(TestSort)
diff --git a/sheets/tests/TestSort.h b/sheets/tests/TestSort.h
index f70ee984636..6a63000a0f9 100644
--- a/sheets/tests/TestSort.h
+++ b/sheets/tests/TestSort.h
@@ -1,43 +1,42 @@
/* This file is part of the KDE project
Copyright 2011 Juan Aquino <utcl95@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_SORT
#define CALLIGRA_SHEETS_TEST_SORT
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class TestSort : public QObject
{
Q_OBJECT
private Q_SLOTS:
void AscendingOrder();
void DescendingOrder();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_SORT
diff --git a/sheets/tests/TestStatisticalFunctions.cpp b/sheets/tests/TestStatisticalFunctions.cpp
index 8a4aed84feb..c6478d70fcd 100644
--- a/sheets/tests/TestStatisticalFunctions.cpp
+++ b/sheets/tests/TestStatisticalFunctions.cpp
@@ -1,1251 +1,1251 @@
/* This file is part of the KDE project
Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestStatisticalFunctions.h"
#include <math.h>
-#include "qtest_kde.h"
+#include <QTest>
#include <CellStorage.h>
#include <Formula.h>
#include <Map.h>
#include <Sheet.h>
#include "TestKspreadCommon.h"
using namespace Calligra::Sheets;
// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
// - different algorithms among spreadsheet programs
// - precision limitation of floating-point number representation
// - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x,y) QCOMPARE(TestDouble(x,y,6),y)
#define CHECK_EVAL_SHORT(x,y) QCOMPARE(TestDouble(x,y,10),y)
#define CHECK_ARRAY(x,y) QCOMPARE(TestArray(x,y,10),true)
#define CHECK_ARRAY_NOSIZE(x,y) QCOMPARE(TestArray(x,y,10,false),true)
#define ROUND(x) (roundf(1e15 * x) / 1e15)
bool TestStatisticalFunctions::TestArray(const QString& formula, const QString& _Array, int accuracy, bool checkSize = true)
{
// define epsilon
double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));
Value Array = evaluate(_Array);
// kDebug()<<"Array = "<<Array;
Value result = evaluate(formula);
// test match size
if (checkSize)
if (Array.rows() != result.rows() || Array.columns() != result.columns()) {
kDebug() << "Array size do not match";
return false;
}
// if checkSize is disabled the count of Array array could be lower than result array
for (int e = 0; e < (int)Array.count(); e++) {
kDebug() << "check element (" << e << ") " << (double)Array.element(e).asFloat() << " " << (double)result.element(e).asFloat();
bool res = (long double) fabsl(Array.element(e).asFloat() - result.element(e).asFloat()) < epsilon;
if (!res) {
kDebug() << "check failed -->" << "Element(" << e << ") " << (double)Array.element(e).asFloat() << " to" << (double) result.element(e).asFloat() << " diff =" << (double)(Array.element(e).asFloat() - result.element(e).asFloat());
return false;
}
}
// test passed
return true;
}
Value TestStatisticalFunctions::TestDouble(const QString& formula, const Value& v2, int accuracy)
{
double epsilon = DBL_EPSILON * pow(10.0, (double)(accuracy));
Formula f(m_map->sheet(0)); // bind to test case data set
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
bool res = fabs(v2.asFloat() - result.asFloat()) < epsilon;
if (!res)
kDebug(36002) << "check failed -->" << "Epsilon =" << epsilon << "" << (double)v2.asFloat() << " to" << (double)result.asFloat() << " diff =" << (double)(v2.asFloat() - result.asFloat());
// else
// kDebug(36002)<<"check -->" <<" diff =" << v2.asFloat()-result.asFloat();
if (res)
return v2;
else
return result;
}
// round to get at most 15-digits number
static Value RoundNumber(const Value& v)
{
if (v.isNumber()) {
double d = numToDouble(v.asFloat());
if (fabs(d) < DBL_EPSILON)
d = 0.0;
return Value(ROUND(d));
} else
return v;
}
Value TestStatisticalFunctions::evaluate(const QString& formula)
{
Formula f(m_map->sheet(0));
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
#if 0
// this magically generates the CHECKs
printf(" CHECK_EVAL( \"%s\", %15g) );\n", qPrintable(formula), result.asFloat());
#endif
return RoundNumber(result);
}
void TestStatisticalFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
m_map = new Map(0 /*no Doc*/);
m_map->addNewSheet();
Sheet* sheet = m_map->sheet(0);
CellStorage* storage = sheet->cellStorage();
//
// Test case data set
//
// A19:A29
storage->setValue(1, 19, Value(1));
storage->setValue(1, 20, Value(2));
storage->setValue(1, 21, Value(4));
storage->setValue(1, 22, Value(8));
storage->setValue(1, 23, Value(16));
storage->setValue(1, 24, Value(32));
storage->setValue(1, 25, Value(64));
storage->setValue(1, 26, Value(128));
storage->setValue(1, 27, Value(256));
storage->setValue(1, 28, Value(512));
storage->setValue(1, 29, Value(1024));
storage->setValue(1, 30, Value(2048));
storage->setValue(1, 31, Value(4096));
// B3:B17
storage->setValue(2, 3, Value("7"));
storage->setValue(2, 4, Value(2));
storage->setValue(2, 5, Value(3));
storage->setValue(2, 6, Value(true));
storage->setValue(2, 7, Value("Hello"));
// B8 leave empty
storage->setValue(2, 9, Value::errorDIV0());
storage->setValue(2, 10, Value(0));
storage->setValue(2, 11, Value(3));
storage->setValue(2, 12, Value(4));
storage->setValue(2, 13, Value("2005-0131T01:00:00"));
storage->setValue(2, 14, Value(1));
storage->setValue(2, 15, Value(2));
storage->setValue(2, 16, Value(3));
storage->setValue(2, 17, Value(4));
CHECK_EVAL("AVEDEV(1;2;3;4)", Value(1));
// C4:C6
storage->setValue(3, 4, Value(4));
storage->setValue(3, 5, Value(5));
storage->setValue(3, 6, Value(7));
// C11:C17
storage->setValue(3, 11, Value(5));
storage->setValue(3, 12, Value(6));
storage->setValue(3, 13, Value(8));
storage->setValue(3, 14, Value(4));
storage->setValue(3, 15, Value(3));
storage->setValue(3, 16, Value(2));
storage->setValue(3, 17, Value(1));
// C19:C31
storage->setValue(3, 19, Value(0));
storage->setValue(3, 20, Value(5));
storage->setValue(3, 21, Value(2));
storage->setValue(3, 22, Value(5));
storage->setValue(3, 23, Value(3));
storage->setValue(3, 24, Value(4));
storage->setValue(3, 25, Value(4));
storage->setValue(3, 26, Value(0));
storage->setValue(3, 27, Value(8));
storage->setValue(3, 28, Value(1));
storage->setValue(3, 29, Value(9));
storage->setValue(3, 30, Value(6));
storage->setValue(3, 31, Value(2));
// C51:C57
storage->setValue(3, 51, Value(7));
storage->setValue(3, 52, Value(9));
storage->setValue(3, 53, Value(11));
storage->setValue(3, 54, Value(12));
storage->setValue(3, 55, Value(15));
storage->setValue(3, 56, Value(17));
storage->setValue(3, 57, Value(19));
// D51:D57
storage->setValue(4, 51, Value(100));
storage->setValue(4, 52, Value(105));
storage->setValue(4, 53, Value(104));
storage->setValue(4, 54, Value(108));
storage->setValue(4, 55, Value(111));
storage->setValue(4, 56, Value(120));
storage->setValue(4, 57, Value(133));
// F19:F26
storage->setValue(6, 19, Value(20));
storage->setValue(6, 20, Value(5));
storage->setValue(6, 21, Value(-20));
storage->setValue(6, 22, Value(-60));
storage->setValue(6, 23, Value(75));
storage->setValue(6, 24, Value(-29));
storage->setValue(6, 25, Value(20));
storage->setValue(6, 26, Value(30));
// F51:F60
storage->setValue(6, 51, Value(3));
storage->setValue(6, 52, Value(4));
storage->setValue(6, 53, Value(5));
storage->setValue(6, 54, Value(2));
storage->setValue(6, 55, Value(3));
storage->setValue(6, 56, Value(4));
storage->setValue(6, 57, Value(5));
storage->setValue(6, 58, Value(6));
storage->setValue(6, 59, Value(4));
storage->setValue(6, 60, Value(7));
// G51:G60
storage->setValue(7, 51, Value(23));
storage->setValue(7, 52, Value(24));
storage->setValue(7, 53, Value(25));
storage->setValue(7, 54, Value(22));
storage->setValue(7, 55, Value(23));
storage->setValue(7, 56, Value(24));
storage->setValue(7, 57, Value(25));
storage->setValue(7, 58, Value(26));
storage->setValue(7, 59, Value(24));
storage->setValue(7, 60, Value(27));
// H19:H31
storage->setValue(8, 19, Value("2005-03-12"));
storage->setValue(8, 20, Value("2002-02-03"));
storage->setValue(8, 21, Value("2005-03-08"));
storage->setValue(8, 22, Value("1991-03-27"));
storage->setValue(8, 23, Value("1967-07-05"));
storage->setValue(8, 24, Value("1912-12-23"));
storage->setValue(8, 25, Value("1992-02-06"));
storage->setValue(8, 26, Value("1934-07-04"));
storage->setValue(8, 27, Value("1909-01-08"));
storage->setValue(8, 28, Value("1989-11-28"));
storage->setValue(8, 29, Value("2000-02-22"));
storage->setValue(8, 30, Value("2004-03-29"));
storage->setValue(8, 31, Value("1946-07-13"));
// I19:I31
storage->setValue(9, 19, Value(13));
storage->setValue(9, 20, Value(12));
storage->setValue(9, 21, Value(11));
storage->setValue(9, 22, Value(10));
storage->setValue(9, 23, Value(9));
storage->setValue(9, 24, Value(8));
storage->setValue(9, 25, Value(7));
storage->setValue(9, 26, Value(6));
storage->setValue(9, 27, Value(5));
storage->setValue(9, 28, Value(4));
storage->setValue(9, 29, Value(3));
storage->setValue(9, 30, Value(2));
storage->setValue(9, 31, Value(1));
}
void TestStatisticalFunctions::testAVEDEV()
{
// ODF-tests
CHECK_EVAL("AVEDEV(1;2;3;4)", Value(1)); //
}
void TestStatisticalFunctions::testAVERAGE()
{
// ODF-tests
CHECK_EVAL("AVERAGE(2; 4)", Value(3)); //
}
void TestStatisticalFunctions::testAVERAGEA()
{
// ODF-tests
CHECK_EVAL("AVERAGEA(2; 4)", Value(3)); //
CHECK_EVAL("AVERAGEA(TRUE(); FALSE(); 5)", Value(2)); //
}
void TestStatisticalFunctions::testBETADIST()
{
// ODF-tests
// Cumulative tests
CHECK_EVAL("BETADIST( 0 ; 3; 4)", Value(0)); //
CHECK_EVAL("BETADIST( 0.5; 3; 4)", Value(0.656250)); //
CHECK_EVAL("BETADIST( 0.9; 4; 3)", Value(0.984150)); //
CHECK_EVAL("BETADIST( 2 ; 3; 4)", Value(1)); // constraints x > b should be 1 if cumulative
CHECK_EVAL("BETADIST(-1 ; 3; 4)", Value(0)); // constraints x < a
CHECK_EVAL_SHORT("BETADIST(1.5;3;4;1;2)", evaluate("BETADIST(0.5;3;4)")); // diff = -2.27021e-09
CHECK_EVAL_SHORT("BETADIST(2;3;4;1;3)", evaluate("BETADIST(0.5;3;4)")); // diff = -2.27021e-09
// last parameter FALSE (non - Cumulative)
CHECK_EVAL("BETADIST( 0 ;3;4;0;1;FALSE())", Value(0)); //
CHECK_EVAL("BETADIST( 0.5;3;4;0;1;FALSE())", Value(0.0005208333)); // 0.000521
CHECK_EVAL("BETADIST( 0.9;4;3;0;1;FALSE())", Value(0.0001215000)); // 0.000122
CHECK_EVAL("BETADIST( 2 ;3;4;0;1;FALSE())", Value(0)); // constraints x > b should be 0 if non-cumulative
CHECK_EVAL("BETADIST(-1 ;3;4;0;1;FALSE())", Value(0)); // constraints x < a
CHECK_EVAL("BETADIST(1.5;3;4;1;2;FALSE())", evaluate("BETADIST(0.5;3;4;0;1;FALSE())")); //
CHECK_EVAL("BETADIST(2 ;3;4;1;3;FALSE())", evaluate("BETADIST(0.5;3;4;0;1;FALSE())")); //
}
void TestStatisticalFunctions::testBETAINV()
{
// ODF-tests
CHECK_EVAL("BETADIST(BETAINV(0;3;4);3;4)", Value(0)); //
CHECK_EVAL("BETADIST(BETAINV(0.1;3;4);3;4)", Value(0.1)); //
CHECK_EVAL("BETADIST(BETAINV(0.3;3;4);3;4)", Value(0.3)); //
CHECK_EVAL("BETADIST(BETAINV(0.5;4;3);4;3)", Value(0.5)); //
CHECK_EVAL("BETADIST(BETAINV(0.7;4;3);4;3)", Value(0.7)); //
CHECK_EVAL("BETADIST(BETAINV(1;3;4);3;4)", Value(1)); //
CHECK_EVAL("BETADIST(BETAINV(0;3;4;1;3);3;4;1;3)", Value(0)); //
CHECK_EVAL("BETADIST(BETAINV(0.1;3;4;1;3);3;4;1;3)", Value(0.1)); //
CHECK_EVAL("BETADIST(BETAINV(0.3;3;4;1;3);3;4;1;3)", Value(0.3)); //
CHECK_EVAL("BETADIST(BETAINV(0.5;4;3;1;3);4;3;1;3)", Value(0.5)); //
CHECK_EVAL("BETADIST(BETAINV(0.7;4;3;1;3);4;3;1;3)", Value(0.7)); //
CHECK_EVAL("BETADIST(BETAINV(1;3;4;1;3);3;4;1;3)", Value(1)); //
}
void TestStatisticalFunctions::testBINOMDIST()
{
// bettersolution.com
CHECK_EVAL("BINOMDIST(10;10; 1 ;0)", Value(1)); // Prob.=100% - all trials successful
CHECK_EVAL("BINOMDIST(9 ; 1; 10 ;0)", Value(0)); // Prob. of -exactly- 9 trials successful is 0 then
CHECK_EVAL("BINOMDIST(10;10; 0.1;1)", Value(1)); // Sum of probabilities of 0..10 hits is 1.
// CHECK_EVAL("BINOMDIST(4 ;10; 0.4;1)", Value( 0.6331032576 ) ); // Some random values.
// my tests
CHECK_EVAL_SHORT("BINOMDIST(4 ;10; 0.4;1)", Value(0.6331032576)); // Some random values.
CHECK_EVAL_SHORT("BINOMDIST(5 ;10; 0.4;1)", Value(0.8337613824)); // Some random values.
CHECK_EVAL_SHORT("BINOMDIST(6 ;10; 0.4;1)", Value(0.9452381184)); // Some random values.
CHECK_EVAL_SHORT("BINOMDIST(4 ;10; 0.2;1)", Value(0.9672065024)); // Some random values.
CHECK_EVAL_SHORT("BINOMDIST(5 ;10; 0.2;1)", Value(0.9936306176)); // Some random values.
CHECK_EVAL_SHORT("BINOMDIST(6 ;10; 0.2;1)", Value(0.9991356416)); // Some random values.
}
void TestStatisticalFunctions::testCHIDIST()
{
// bettersolution.com
CHECK_EVAL("CHIDIST( 18.307;10)", Value(0.0500005892)); //
CHECK_EVAL("CHIDIST( 2;2)", Value(0.3678794412)); //
CHECK_EVAL("CHIDIST( -1;2)", Value(1)); // constraint x<0 TODO EXCEL return #NUM!
// CHECK_EVAL("CHIDIST( 4;\"texr\")", Value::VALUE() ); // TODO
}
void TestStatisticalFunctions::testCONFIDENCE()
{
// ODF-tests
CHECK_EVAL("CONFIDENCE(0.5 ; 1;1)", Value(0.67448975)); //
CHECK_EVAL("CONFIDENCE(0.25; 1;1)", Value(1.1503493804)); //
CHECK_EVAL("CONFIDENCE(0.5 ; 4;1)", Value(2.6979590008)); // Multiplying stddev by X multiplies result by X.
CHECK_EVAL("CONFIDENCE(0.5 ; 1;4)", Value(0.3372448751)); // Multiplying count by X*X divides result by X.
// check constraints
CHECK_EVAL("CONFIDENCE(-0.5; 1;4)", Value::errorNUM()); // 0 < alpha < 1
CHECK_EVAL("CONFIDENCE( 1.5; 1;4)", Value::errorNUM()); // 0 < alpha < 1
CHECK_EVAL("CONFIDENCE( 0.5;-1;4)", Value::errorNUM()); // stddev > 0
CHECK_EVAL("CONFIDENCE( 0.5; 1;0)", Value::errorNUM()); // size >= 1
}
void TestStatisticalFunctions::testCORREL()
{
// Cell | Value Cell | Value
// ------+------ ------+------
// B14 | 1 C14 | 4
// B15 | 2 C15 | 3
// B16 | 3 C16 | 2
// B17 | 4 C17 | 1
// ODF-tests
CHECK_EVAL("CORREL(B14:B17;B14:B17)", Value(1)); // Perfect positive correlation given identical sequences
CHECK_EVAL("CORREL(B14:B17;C14:C17)", Value(-1)); // Perfect negative correlation given reversed sequences
CHECK_EVAL("CORREL(1;2)", Value::errorNUM()); // Each list must contain at least 2 values
CHECK_EVAL("CORREL(B14:B16;B15:B16)", Value::errorNUM()); // The length of each list must be equal
}
void TestStatisticalFunctions::testCOVAR()
{
// Cell | Value Cell | Value
// ------+------ ------+------
// B14 | 1 C14 | 4
// B15 | 2 C15 | 3
// B16 | 3 C16 | 2
// B17 | 4 C17 | 1
// ODF-tests
CHECK_EVAL("COVAR(C11:C17;C11:C17)", Value(4.9795918367)); //
CHECK_EVAL("COVAR(B14:B17;C14:C17)", Value(-1.25)); //
CHECK_EVAL("COVAR(B14:B17;C13:C17)", Value::errorNUM()); // TODO should we check for "array sizes don't match" or "value counts" in array?.
}
void TestStatisticalFunctions::testDEVSQ()
{
// ODF-tests
CHECK_EVAL("DEVSQ(4)", Value(0)); // One value - no deviation.
CHECK_EVAL("DEVSQ(5;5;5;5)", Value(0)); // Identical values - no deviation.
CHECK_EVAL("DEVSQ(2;4)", Value(2)); // Each value deviates by 1.
CHECK_EVAL("DEVSQ(-5;5;-1;1)", Value(52)); // Average=0 must work properly.
CHECK_EVAL("DEVSQ(C11:C17)", Value(34.8571428571)); // Test values.
CHECK_EVAL("DEVSQ(B14:B17)", Value(5.00)); // Test values.
CHECK_EVAL("DEVSQ(B14)", Value(0)); // One value - no deviation.
}
// void TestStatisticalFunctions::testDEVSQA()
// {
// // no test available
// }
void TestStatisticalFunctions::testEXPONDIST()
{
// ODF-tests
CHECK_EVAL("EXPONDIST( 1;1;TRUE())", Value(0.6321205588)); //
CHECK_EVAL("EXPONDIST( 2;2;TRUE())", Value(0.9816843611)); //
CHECK_EVAL("EXPONDIST( 0;1;TRUE())", Value(0)); //
CHECK_EVAL("EXPONDIST(-1;1;TRUE())", Value(0)); // constraint x<0
CHECK_EVAL("EXPONDIST( 1;1;FALSE())", Value(0.3678794412)); //
CHECK_EVAL("EXPONDIST( 2;2;FALSE())", Value(0.0366312778)); //
CHECK_EVAL("EXPONDIST( 0;1;FALSE())", Value(1)); //
CHECK_EVAL("EXPONDIST(-1;1;FALSE())", Value(0)); // constraint x<0
// test disabled, because 3rd param. is not opt.!
//CHECK_EVAL("EXPONDIST(1;1)", evaluate("EXPONDIST(1;1;TRUE())") );
}
void TestStatisticalFunctions::testFDIST()
{
// ODF-tests
// cumulative
CHECK_EVAL("FDIST( 1;4;5)", Value(0.5143428033)); //
CHECK_EVAL("FDIST( 2;5;4)", Value(0.7392019723)); //
CHECK_EVAL("FDIST( 0;4;5)", Value(0)); //
CHECK_EVAL("FDIST(-1;4;5)", Value(0)); //
CHECK_EVAL_SHORT("FDIST( 1;4;5;TRUE())", evaluate("FDIST(1;4;5)")); // diff = -1.39644e-09
// non-cumulative
CHECK_EVAL("FDIST( 1;4;5;FALSE())", Value(0.3976140792)); //
CHECK_EVAL("FDIST( 2;5;4;FALSE())", Value(0.1540004108)); //
CHECK_EVAL("FDIST( 0;4;5;FALSE())", Value(0)); //
CHECK_EVAL("FDIST(-1;4;5;FALSE())", Value(0)); //
}
void TestStatisticalFunctions::testFINV()
{
// ODF-tests
CHECK_EVAL("FDIST(FINV(0.1;3;4);3;4)", Value(0.1)); //
CHECK_EVAL("FDIST(FINV(0.3;3;4);3;4)", Value(0.3)); //
CHECK_EVAL("FDIST(FINV(0.5;3;4);3;4)", Value(0.5)); //
CHECK_EVAL("FDIST(FINV(0.7;3;4);3;4)", Value(0.7)); //
CHECK_EVAL("FDIST(FINV(0.0;3;4);3;4)", Value(0.0)); //
}
void TestStatisticalFunctions::testFISHER()
{
// ODF-tests
CHECK_EVAL("FISHER(0)", Value(0)); // Fisher of 0.
CHECK_EVAL("FISHER((EXP(1)-1)/(EXP(1)+1))", Value(0.5)); // Argument chosen so that ln=1
CHECK_EVAL_SHORT("FISHER(0.5)", Value(0.54930614)); // TODO - be more precise - Some random value.
CHECK_EVAL("FISHER(0.47)+FISHER(-0.47)", Value(0)); // Function is symetrical.
}
void TestStatisticalFunctions::testFISHERINV()
{
// ODF-tests
CHECK_EVAL("FISHERINV(0)", Value(0)); // Fisherinv of 0.
CHECK_EVAL("FISHERINV(LN(2))", Value(0.6)); // e^(2*ln(2))=4
CHECK_EVAL("FISHERINV(FISHER(0.5))", Value(0.5)); // Some random value.
CHECK_EVAL("FISHERINV(0.47)+FISHERINV(-0.47)", Value(0)); // Function is symetrical.
}
void TestStatisticalFunctions::testFREQUENCY()
{
Value result(Value::Array);
result.setElement(0, 0, Value(3));
result.setElement(0, 1, Value(2));
result.setElement(0, 2, Value(4));
result.setElement(0, 3, Value(1));
CHECK_EVAL("FREQUENCY({1;2;3;4;5;6;7;8;9;10};{3|5|9})", result);
// the second arg has to be a column vector
CHECK_EVAL("ISERROR(FREQUENCY({1;2;3;4;5;6;7;8;9;10};{3;5;9}))", Value(true));
// an empty second arg returns the overall number count
CHECK_EVAL("FREQUENCY({1;2;3;4;5;6;7;8;9;10};)", Value(10));
}
void TestStatisticalFunctions::testFTEST()
{
// TODO - be more precise
// ODF-tests
CHECK_EVAL_SHORT("FTEST(B14:B17; C14:C17)", Value(1.0)); // Same data (second reversed),
CHECK_EVAL_SHORT("FTEST(B14:B15; C13:C14)", Value(0.311916521)); // Significantly different variances,
// so less likely to come from same data set.
}
void TestStatisticalFunctions::testGAMMADIST()
{
// bettersolution.com non-cumulative
CHECK_EVAL("GAMMADIST(10 ;9;2;FALSE())", Value(0.0326390197)); //
// bettersolution.com cumulative
CHECK_EVAL("GAMMADIST(10 ;9;2;TRUE())", Value(0.0680936347)); //
CHECK_EVAL("GAMMADIST(10 ;10;5;TRUE())", Value(0.0000464981)); // Bettersolution = 0 .rounded?
CHECK_EVAL("GAMMADIST(7 ;5;1;TRUE())", Value(0.8270083921)); // TODO NOK / Bettersolution = 1
// bettersolution.com constraints
CHECK_EVAL("GAMMADIST(10 ;9;0;TRUE())", Value::errorNUM()); // beta = 0 not allowed
CHECK_EVAL("GAMMADIST(10 ;-2;2;TRUE())", Value::errorNUM()); // was wird getestet? alpha
CHECK_EVAL("GAMMADIST(-1 ;9;2;TRUE())", Value::errorNUM()); // NOK
CHECK_EVAL("GAMMADIST(7 ;\"text\";1;TRUE())", Value::errorVALUE()); // text not allowed
CHECK_EVAL("GAMMADIST(7 ;5;\"text\";TRUE())", Value::errorVALUE()); // text not allowed
// ODF-tests non-cumulative
CHECK_EVAL("GAMMADIST(0 ;3;4;FALSE())", Value(0));
CHECK_EVAL("GAMMADIST(0.5;3;4;FALSE())", Value(0.0017236268)); //
CHECK_EVAL("GAMMADIST(9 ;4;3;FALSE())", Value(0.0746806026)); // ODF-Specs -> 0.0666979468 should be 0,0746
CHECK_EVAL("GAMMADIST(0 ;3;4;FALSE())", Value(0));
CHECK_EVAL("GAMMADIST(9 ;4;3;FALSE())", Value(0.0746806026)); // TODO check ODF-Specs -> 0.390661
// ODF-tests cumulative
CHECK_EVAL("GAMMADIST(0.5;3;4;TRUE())", Value(0.0002964775)); //
CHECK_EVAL("GAMMADIST(9 ;4;3;TRUE())", Value(0.3527681112));
CHECK_EVAL("GAMMADIST(-1 ;4;3;TRUE())", Value(0)); // neg. x return always 0
CHECK_EVAL("GAMMADIST(-1 ;3;4;FALSE())", Value(0)); // neg. x return always 0
// various tests cumulative
CHECK_EVAL("GAMMADIST(9 ;9;2;TRUE())", Value(0.0402573125)); //
CHECK_EVAL("GAMMADIST(9 ;8;2;TRUE())", Value(0.0865864716)); //
}
void TestStatisticalFunctions::testGAMMAINV()
{
// ODF-tests
CHECK_EVAL("GAMMADIST(GAMMAINV(0.1;3;4);3;4;1)", Value(0.1)); //
CHECK_EVAL("GAMMADIST(GAMMAINV(0.3;3;4);3;4;1)", Value(0.3)); //
CHECK_EVAL("GAMMADIST(GAMMAINV(0.5;3;4);3;4;1)", Value(0.5)); //
CHECK_EVAL("GAMMADIST(GAMMAINV(0.7;3;4);3;4;1)", Value(0.7)); //
CHECK_EVAL("GAMMADIST(GAMMAINV(0 ;3;4);3;4;1)", Value(0)); //
}
void TestStatisticalFunctions::testGAUSS()
{
// ODF-tests
CHECK_EVAL("GAUSS(0)", Value(0)); // Mean of one value.
CHECK_EVAL("GAUSS(1)", Value(0.341344746)); // Multiple equivalent values.
// my test
CHECK_EVAL("GAUSS(-0.25)", Value(-0.0987063257)); // check neg. values. test for fixes gauss_func
}
void TestStatisticalFunctions::testGROWTH()
{
// constraints
CHECK_EVAL("GROWTH({}; C19:C23; 1)", Value::errorNA()); // empty knownY matrix
CHECK_EVAL("GROWTH({5.0;\"a\"}; C19:C23; 1)", Value::errorNA()); // knownY matrix constains chars
// ODF-tests
CHECK_ARRAY("GROWTH( A19:A23; C19:C23; 1 )", "{2.5198420998}"); // with offset
CHECK_ARRAY("GROWTH( A19:A23; C19:C23; 1; FALSE() )", "{1.4859942891}"); // without offset
// http://www.techonthenet.com/excel/formulas/growth.php
CHECK_ARRAY("GROWTH({4;5;6};{10;20;30};{15;30;45})", "{4.4569483434;6.0409611796;8.1879369384}"); //
CHECK_ARRAY("GROWTH({4;5;6};{10;20;30})", "{4.0273074534;4.9324241487;6.0409611796}"); //
CHECK_ARRAY_NOSIZE("GROWTH({4;5;6})", "{4.0273074534}"); //
}
void TestStatisticalFunctions::testGEOMEAN()
{
// ODF-tests
CHECK_EVAL("GEOMEAN(7)", Value(7)); // Mean of one value.
CHECK_EVAL("GEOMEAN(5;5;5;5)", Value(5)); // Multiple equivalent values.
CHECK_EVAL("GEOMEAN(2;8;2;8)", Value(4)); // Some values.
CHECK_EVAL("GEOMEAN(8;0;8;8;8;8)", Value::errorNUM()); // Error if there is a 0 in the range.
CHECK_EVAL("GEOMEAN(C11)", Value(5)); // One value, range.
CHECK_EVAL("GEOMEAN(C11:C17)", Value(3.4451109418)); // Some values, range.
CHECK_EVAL("GEOMEAN(B14:B17)", Value(2.2133638394)); // Some values, range.
}
void TestStatisticalFunctions::testHARMEAN()
{
// ODF-tests
CHECK_EVAL("HARMEAN(7)", Value(7)); // Mean of one value.
CHECK_EVAL("HARMEAN(4;4;4;4)", Value(4)); // Multiple equivalent values.
CHECK_EVAL("HARMEAN(2;4;4)", Value(3)); // Some values.
CHECK_EVAL("HARMEAN(8;0;8;8;8;8)", Value::errorNUM()); // Error if there is a 0 in the range.
CHECK_EVAL("HARMEAN(C11)", Value(5)); // One value, range.
CHECK_EVAL("HARMEAN(C11:C17)", Value(2.7184466019)); // Some values, range.
CHECK_EVAL("HARMEAN(B14:B17)", Value(1.92)); // Some values, range.
}
void TestStatisticalFunctions::testHYPGEOMDIST()
{
// ODF-tests
CHECK_EVAL("HYPGEOMDIST( 2 ;3;3;6;FALSE())", Value(0.45)); // If an urn contains 3 red balls and 3 green balls, the probability
// that 2 red balls will be selected after 3 selections without replacement.
// (0.45=27/60).
CHECK_EVAL("HYPGEOMDIST( 2 ;3;3;6)", Value(0.45)); // The default for cumulative is FALSE().
CHECK_EVAL("HYPGEOMDIST( 0 ;3;3;6)", Value(0.05)); // There is a small (5%) chance of selecting only green balls.
CHECK_EVAL("HYPGEOMDIST( 2 ;3;3;6;TRUE())", Value(0.95)); // The probability of selecting at most two red balls (i.e 0, 1 or 2).
CHECK_EVAL("HYPGEOMDIST( 4 ;3;3;6)", Value::errorNUM()); // X must be <= M
CHECK_EVAL("HYPGEOMDIST( 2.8;3;3;6)", Value(0.45)); // Non-integers are truncated.
CHECK_EVAL("HYPGEOMDIST(-2 ;3;3;6)", Value::errorNUM()); // Values must be >= 0.
CHECK_EVAL("HYPGEOMDIST( 0 ;0;0;0)", Value(1)); //
}
void TestStatisticalFunctions::testINTERCEPT()
{
// bettersolution.com
CHECK_EVAL_SHORT("INTERCEPT({2;3;9;1;8};{6;5;11;7;5})", Value(0.048387097)); // TODO - be more precise
// CHECK_EVAL_SHORT("INTERCEPT({2;4;6};{6;3;8})", Value( 2.21053 ) ); // TODO - be more precise
CHECK_EVAL("INTERCEPT({2;3;9};{6;5;11;7;5})", Value::errorNUM()); //
CHECK_EVAL("INTERCEPT(\"text\";{6;5;11;7;5})", Value::errorNUM()); // text is not allowed
}
void TestStatisticalFunctions::testKURT()
{
// TODO check function
// ODF-tests
CHECK_EVAL("KURT(C20:C25)", Value(-0.446162998)); //
CHECK_EVAL("KURT(C20:C23;4;4)", Value(-0.446162998)); //
}
void TestStatisticalFunctions::testLARGE()
{
// Cell | Value | N'th
// ------+-------+------
// B14 | 1 | 3
// B15 | 2 | 2
// B16 | 3 | 1
// ODF-tests
CHECK_EVAL("LARGE(B14:B16;1)", Value(3)); //
CHECK_EVAL("LARGE(B14:B16;3)", Value(1)); //
CHECK_EVAL("LARGE(B14:B16;4)", Value::errorNUM()); // N is greater than the length of the list
}
void TestStatisticalFunctions::testLEGACYCHIDIST()
{
// ODF-tests LEGACY.CHIDIST
CHECK_EVAL("LEGACYCHIDIST(-1;2)", Value(1)); // constraint x<0
CHECK_EVAL("LEGACYCHIDIST( 0;2)", Value(1)); // constraint x=0
CHECK_EVAL("LEGACYCHIDIST( 2;2)", Value(0.3678794412)); //
CHECK_EVAL("LEGACYCHIDIST( 4;4)", Value(0.4060058497)); //
}
void TestStatisticalFunctions::testLEGACYCHIINV()
{
// ODF-tests LEGACY.CHIINV
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.1;3);3)", Value(0.1)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.3;3);3)", Value(0.3)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.5;3);3)", Value(0.5)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.7;3);3)", Value(0.7)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.9;3);3)", Value(0.9)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.1;20);20)", Value(0.1)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.3;20);20)", Value(0.3)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.5;20);20)", Value(0.5)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.7;20);20)", Value(0.7)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(0.9;20);20)", Value(0.9)); //
CHECK_EVAL("LEGACYCHIDIST(LEGACYCHIINV(1.0;20);20)", Value(1.0)); //
}
void TestStatisticalFunctions::testLEGACYFDIST()
{
// ODF-tests
CHECK_EVAL("LEGACYFDIST( 1;4;5)", Value(0.4856571967)); //
CHECK_EVAL("LEGACYFDIST( 2;5;4)", Value(0.2607980277)); //
CHECK_EVAL("LEGACYFDIST( 0;4;5)", Value(1)); //
CHECK_EVAL("LEGACYFDIST(-1;4;5)", Value::errorNUM()); //
}
void TestStatisticalFunctions::testLEGACYFINV()
{
// ODF-tests
CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.1;3;4);3;4)", Value(0.1)); //
CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.3;3;4);3;4)", Value(0.3)); //
CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.5;3;4);3;4)", Value(0.5)); //
CHECK_EVAL("LEGACYFDIST(LEGACYFINV(0.7;3;4);3;4)", Value(0.7)); //
CHECK_EVAL("LEGACYFDIST(LEGACYFINV(1.0;3;4);3;4)", Value(1.0)); //
}
void TestStatisticalFunctions::testLOGINV()
{
// TODO check function
// ODF-tests
CHECK_EVAL("LOGNORMDIST(LOGINV(0.1;0;1);0;1;TRUE())", Value(0.1)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.3;0;1);0;1;TRUE())", Value(0.3)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.5;0;1);0;1;TRUE())", Value(0.5)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.7;0;1);0;1;TRUE())", Value(0.7)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.9;0;1);0;1;TRUE())", Value(0.9)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.1;1;4);1;4;TRUE())", Value(0.1)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.3;1;4);1;4;TRUE())", Value(0.3)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.5;1;4);1;4;TRUE())", Value(0.5)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.7;1;4);1;4;TRUE())", Value(0.7)); //
CHECK_EVAL("LOGNORMDIST(LOGINV(0.9;1;4);1;4;TRUE())", Value(0.9)); //
CHECK_EVAL("LOGINV(0.5)", Value(1)); //
}
void TestStatisticalFunctions::testLOGNORMDIST()
{
// TODO - implement cumulative calculation
// - check definition cumulative/non-cumulative and constraints
// ODF-tests
// cumulative
CHECK_EVAL("LOGNORMDIST(1)", Value(0.5)); //
CHECK_EVAL("LOGNORMDIST(1;1;4)", Value(0.4012936743)); //
CHECK_EVAL("LOGNORMDIST(1;0;1;TRUE())", Value(0.5)); //
CHECK_EVAL("LOGNORMDIST(1;1;4;TRUE())", Value(0.4012936743)); //
CHECK_EVAL("LOGNORMDIST(1;-1;4;TRUE())", Value(0.5987063257)); //
// CHECK_EVAL("LOGNORMDIST(2;-1;4;TRUE())", Value( 0.663957 ) ); // ??????
CHECK_EVAL("LOGNORMDIST(3;0;1;TRUE())", Value(0.8640313924)); //
CHECK_EVAL("LOGNORMDIST(100;0;1;TRUE())", Value(0.9999979394)); //
CHECK_EVAL("LOGNORMDIST(-1;0;1;TRUE())", Value(0)); // constraint x<0 returns 0
// non-cumulative
// CHECK_EVAL("LOGNORMDIST( 1; 0; 1;FALSE())", Value( 0.398942 ) ); //
// CHECK_EVAL("LOGNORMDIST( 1; 1; 4;FALSE())", Value( 0.096667 ) ); //
// CHECK_EVAL("LOGNORMDIST( 1;-1; 4;FALSE())", Value( 0.096667 ) ); //
// CHECK_EVAL("LOGNORMDIST( 2;-1; 4;FALSE())", Value( 0.045595 ) ); //
// CHECK_EVAL("LOGNORMDIST(-1; 0; 1;FALSE())", Value::errorNUM() ); // constraint failure
// CHECK_EVAL("LOGNORMDIST( 1; 0;-1;FALSE())", Value::errorNUM() ); // constraint failure
}
void TestStatisticalFunctions::testMAX()
{
// Cell | Value Cell | Value
// ------+------ ------+------
// B3 | "7" C14 | 4
// B4 | 2 C15 | 3
// B5 | 3 C16 | 2
// B6 | true C17 | 1
// B7 | "Hello"
// B8 |
// B9 | DIV/0
// ODF-tests
CHECK_EVAL("MAX(2;4;1;-8)", Value(4)); // Negative numbers are smaller than positive numbers.
CHECK_EVAL("MAX(B4:B5)", Value(3)); // The maximum of (2,3) is 3.
// CHECK_EVAL("ISNA(MAXA(NA())", Value(true)); // nline errors are propagated.
CHECK_EVAL("MAX(B3:B5)", Value(3)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("MAX(-1;B7)", Value(-1)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("MAX(B3:B9)", Value::errorVALUE()); // TODO check function - Errors inside ranges are NOT ignored.
}
void TestStatisticalFunctions::testMAXA()
{
// ODF-tests
CHECK_EVAL("MAXA(2;4;1;-8)", Value(4)); // Negative numbers are smaller than positive numbers.
CHECK_EVAL("MAXA(B4:B5)", Value(3)); // The maximum of (2,3) is 3.
// CHECK_EVAL("ISNA(MAXA(NA())", Value(true)); // Inline errors are propagated.
// TODO check function - inline Text must be converted, but not Text in Cells
// CHECK_EVAL("MAXA(B3:B5)", Value( 3 ) ); // Cell text is converted to 0.
CHECK_EVAL("MAXA(-1;B7)", Value(0)); // Cell text is converted to 0.
CHECK_EVAL("MAXA(\"a\")", Value::errorVALUE()); // Text inline is NOT ignored.
CHECK_EVAL("MAXA(B3:B9)", Value::errorVALUE()); // TODO check function - Errors inside ranges are NOT ignored.
CHECK_EVAL("MAXA(B6:B7)", Value(1)); // Logicals are considered numbers.
}
void TestStatisticalFunctions::testMEDIAN()
{
// ODF-tests
CHECK_EVAL("=MEDIAN(10.5;7.2)", Value(8.85));
CHECK_EVAL("=MEDIAN(7.2;200;5.4;45)", Value(26.1));
CHECK_EVAL("=MEDIAN(7.2;200;5.4;8.1)", Value(7.65));
CHECK_EVAL("=MEDIAN(1;3;13;14;15)", Value(13.0));
CHECK_EVAL("=MEDIAN(1;3;13;14;15;35)", Value(13.5));
// Bug 148574: MEDIAN function gives incorrect results
CHECK_EVAL("=MEDIAN(1;2;3)", Value(2));
CHECK_EVAL("=MEDIAN(1;2;3;4;5)", Value(3));
}
void TestStatisticalFunctions::testMIN()
{
// ODF-tests
CHECK_EVAL("MIN(2;4;1;-8)", Value(-8)); // Negative numbers are smaller than positive numbers.
CHECK_EVAL("MIN(B4:B5)", Value(2)); // The minimum of (2,3) is 2.
CHECK_EVAL("MIN(B3)", Value(0)); // If no numbers are provided in all ranges, MIN returns 0
CHECK_EVAL("MIN(\"a\")", Value::errorNUM()); // Non-numbers inline are NOT ignored.
CHECK_EVAL("MIN(B3:B5)", Value(2)); // Cell text is not converted to numbers and is ignored.
}
void TestStatisticalFunctions::testMINA()
{
// ODF-tests
CHECK_EVAL("MINA(2;4;1;-8)", Value(-8)); // Negative numbers are smaller than positive numbers.
CHECK_EVAL("MINA(B4:B5)", Value(2)); // The minimum of (2,3) is 2.
CHECK_EVAL("MINA(1;B7)", Value(0)); // Cell text is converted to 0.
CHECK_EVAL("MINA(\"a\")", Value::errorNUM()); // Cell text inline is NOT ignored.
// TODO check function - inline Text must be converted, but not Text in Cells
// CHECK_EVAL("MINA(B3:B5)", Value( 0 ) ); // Cell text is converted to 0.
CHECK_EVAL("MINA(B6:C6)", Value(1)); // The value "True" is considered equivalent to 1.
}
void TestStatisticalFunctions::testMODE()
{
// ODF-tests
CHECK_EVAL("MODE(F51:F60)", Value(4)); //
CHECK_EVAL("MODE(G51;G52;G53;G54;G55;G56;G57;G58;G59;G60)", Value(24)); //
CHECK_EVAL("MODE(1;2;3;4;5;6;7;8;9;10)", Value::errorNUM()); //
}
void TestStatisticalFunctions::testNEGBINOMDIST()
{
// ODF-test
// CHECK_EVAL("NEGBINOMDIST(F20;I29;H6)", Value( 0.000130947 ) ); //
// bettersolutions.com
CHECK_EVAL("NEGBINOMDIST( 0;1; 0.25)", Value(0.25)); //
CHECK_EVAL("NEGBINOMDIST( 0;1; 0.5)", Value(0.5)); //
CHECK_EVAL("NEGBINOMDIST( 1;6; 0.5)", Value(0.046875)); //
CHECK_EVAL("NEGBINOMDIST(10;5; 0.25)", Value(0.0550486604)); //
CHECK_EVAL("NEGBINOMDIST(10;5;-4)", Value::errorNUM()); //
// CHECK_EVAL("NEGBINOMDIST(10;"text";0.25)", Value::NUM() ); //
}
void TestStatisticalFunctions::testNORMDIST()
{
// ODF-tests
CHECK_EVAL("NORMDIST(0;1;4;TRUE())", Value(0.4012936743)); //
CHECK_EVAL("NORMDIST(0;0;1;FALSE())", Value(0.3989422804)); //
CHECK_EVAL("NORMDIST(0;0;1;TRUE())", Value(0.5)); //
CHECK_EVAL("NORMDIST(0;1;4;FALSE())", Value(0.0966670292)); //
CHECK_EVAL("NORMDIST(0;-1;4;FALSE())", Value(0.0966670292)); //
CHECK_EVAL("NORMDIST(0;-1;4;TRUE())", Value(0.5987063257)); //
CHECK_EVAL("NORMDIST(1;-1;4;FALSE())", Value(0.0880163317)); //
CHECK_EVAL("NORMDIST(1;-1;4;TRUE())", Value(0.6914624613)); //
CHECK_EVAL("NORMDIST(1.281552;0;1;TRUE())", Value(0.9000000762)); //
CHECK_EVAL("NORMDIST(0;-1.281552;1;TRUE())", Value(0.9000000762)); //
CHECK_EVAL("NORMDIST(0;0;-1;FALSE())", Value::errorNUM()); //
}
void TestStatisticalFunctions::testNORMINV()
{
// ODF-tests
CHECK_EVAL("NORMDIST(NORMINV(0.1;0;1);0;1;TRUE())", Value(0.1)); //
CHECK_EVAL("NORMDIST(NORMINV(0.3;0;1);0;1;TRUE())", Value(0.3)); //
CHECK_EVAL("NORMDIST(NORMINV(0.5;0;1);0;1;TRUE())", Value(0.5)); //
CHECK_EVAL("NORMDIST(NORMINV(0.7;0;1);0;1;TRUE())", Value(0.7)); //
CHECK_EVAL("NORMDIST(NORMINV(0.9;0;1);0;1;TRUE())", Value(0.9)); //
CHECK_EVAL("NORMDIST(NORMINV(0.1;1;4);1;4;TRUE())", Value(0.1)); //
CHECK_EVAL("NORMDIST(NORMINV(0.3;1;4);1;4;TRUE())", Value(0.3)); //
CHECK_EVAL("NORMDIST(NORMINV(0.5;1;4);1;4;TRUE())", Value(0.5)); //
CHECK_EVAL("NORMDIST(NORMINV(0.7;1;4);1;4;TRUE())", Value(0.7)); //
CHECK_EVAL("NORMDIST(NORMINV(0.9;1;4);1;4;TRUE())", Value(0.9)); //
}
void TestStatisticalFunctions::testPEARSON()
{
// Cell | Value Cell | Value Cell | Value Cell | Value
// ------+------- ------+------- ------+------- ------+-------
// A19 | 1 C19 | 0 C51 | 7 D51 | 100
// A20 | 2 C20 | 5 C51 | 9 D52 | 105
// A21 | 4 C21 | 2 C53 | 11 D53 | 104
// A22 | 8 C22 | 5 C54 | 12 D54 | 108
// A23 | 16 C23 | 3 C55 | 15 D55 | 111
// A24 | 32 C24 | 4 C56 | 17 D56 | 120
// A25 | 64 C25 | 4 C57 | 19 D57 | 133
// A26 | 128 C26 | 0
// A27 | 256 C27 | 8
// A28 | 512 C28 | 1
// A29 | 1024 C29 | 9
// A30 | 2048 C30 | 6
// A31 | 4096 C31 | 2
// ODF-tests
CHECK_EVAL_SHORT("PEARSON(A19:A31;C19:C31)", Value(0.045989147)); //
CHECK_EVAL_SHORT("PEARSON(C51:C57;D51:D57)", Value(0.930164207)); //
CHECK_EVAL("PEARSON(C51:C57;D51:D56)", Value::errorNUM()); //
}
void TestStatisticalFunctions::testPERCENTILE()
{
// ODF-tests
CHECK_EVAL("PERCENTILE(A19:A31;0.38)", Value(24.96)); //
CHECK_EVAL("PERCENTILE(A19:A31;0.95)", Value(2867.2)); //
CHECK_EVAL("PERCENTILE(A19:A31;0.05)", Value(1.6)); //
// my tests
CHECK_EVAL("PERCENTILE(A10:A15;-0.1)", Value::errorVALUE()); //
CHECK_EVAL("PERCENTILE(A19:A25;1.1)", Value::errorVALUE()); //
}
void TestStatisticalFunctions::testPERMUT()
{
// ODF-tests
CHECK_EVAL("PERMUT(2;2)", Value(2)); // =2!/(2-2)!
CHECK_EVAL("PERMUT(4;2)", Value(12)); // =4!/(4-2)!
CHECK_EVAL("PERMUT(4.3;2.1)", Value(12)); // =PERMUT(4;2)
CHECK_EVAL("PERMUT(-4;2)", Value::errorNUM()); //
CHECK_EVAL("PERMUT(4;-2)", Value::errorNUM()); //
}
void TestStatisticalFunctions::testPERMUTATIONA()
{
// ODF-tests
CHECK_EVAL("PERMUTATIONA(64;2)", Value(4096)); //
CHECK_EVAL("PERMUTATIONA(6;3)", Value(216)); //
// my tests
CHECK_EVAL("PERMUTATIONA(0;0)", Value(1)); //
CHECK_EVAL("PERMUTATIONA(-4;2)", Value::errorNUM()); //
CHECK_EVAL("PERMUTATIONA(4;-2)", Value::errorNUM()); //
}
void TestStatisticalFunctions::testPHI()
{
// Cell | Value
// ------+-------
// C23 | 3
// |
// ODF-tests
CHECK_EVAL_SHORT("PHI(C23/10)", Value(0.381387815)); // TODO - be more precise /
CHECK_EVAL_SHORT("PHI(-C23/10)", Value(0.381387815)); // TODO - be more precise /
CHECK_EVAL_SHORT("PHI(0)", Value(0.398942280)); // TODO - be more precise /
}
void TestStatisticalFunctions::testPOISSON()
{
// ODF-tests
CHECK_EVAL_SHORT("POISSON(0;1;FALSE())", Value(0.367880)); // TODO - be more precise /
CHECK_EVAL_SHORT("POISSON(0;2;FALSE())", Value(0.135335)); // TODO - be more precise /
}
void TestStatisticalFunctions::testRANK()
{
// Cell | Value
// ------+------
// A19 | 1
// A20 | 2
// A21 | 4
// A22 | 8
// A23 | 16
// A24 | 32
// A25 | 64
// ODF-tests
CHECK_EVAL("RANK(A20;A19:A25;1)", Value(2)); // ascending
CHECK_EVAL("RANK(A25;A19:A25;0)", Value(1)); // descending
CHECK_EVAL("RANK(A21;A19:A25 )", Value(5)); // ommitted equals descending order
}
void TestStatisticalFunctions::testRSQ()
{
// ODF-tests
CHECK_EVAL("RSQ(H19:H31;I19:I31)", Value(0.075215010)); //
CHECK_EVAL("RSQ(H19:H31;I19:I30)", Value::errorNA()); // array does not have the same size
}
void TestStatisticalFunctions::testQUARTILE()
{
// flag:
// 0 equals MIN()
// 1 25th percentile
// 2 50th percentile equals MEDIAN()
// 3 75th percentile
// 4 equals MAX()
// ODF-tests
CHECK_EVAL("QUARTILE(A19:A25;3)", Value(24)); //
CHECK_EVAL("QUARTILE(F19:F26;1)", Value(-22.25)); //
CHECK_EVAL("QUARTILE(A10:A15;2)", Value::errorVALUE()); //
CHECK_EVAL("QUARTILE(A19:A25;5)", Value::errorVALUE()); // flag > 4
CHECK_EVAL("QUARTILE(F19:F26;1.5)", Value(-22.25)); // 1.5 rounded down to 1
CHECK_EVAL("QUARTILE({1;2;4;8;16;32;64};3)", Value(24)); //
// my tests
CHECK_EVAL("QUARTILE(A19:A25;0)", Value(1)); // MIN()
CHECK_EVAL("QUARTILE(A19:A25;4)", Value(64)); // MAX()
}
void TestStatisticalFunctions::testSKEW()
{
// ODF-tests
CHECK_EVAL_SHORT("SKEW( 1; 2; 4 )", Value(0.935219)); // TODO - be more precise / Expectation value: 2.333333
// Standard deviation: 1.257525
// Third central moment: 0.740741
CHECK_EVAL_SHORT("SKEW(A19:A23)", Value(1.325315)); // TODO - be more precise /
CHECK_EVAL("SKEW( 1; 2 )", Value::errorNUM()); // At least three numbers.
}
void TestStatisticalFunctions::testSKEWP()
{
// ODF-tests
CHECK_EVAL_SHORT("SKEWP( 1; 2; 4 )", Value(0.381802)); // TODO - be more precise / Expectation value: 2.333333
// Standard deviation: 1.247219
// Third central moment: 0.740741
CHECK_EVAL_SHORT("SKEWP(A19:A23)", Value(0.889048)); // TODO - be more precise /
CHECK_EVAL("SKEW( 1; 2 )", Value::errorNUM()); // At least three numbers.
}
void TestStatisticalFunctions::testSLOPE()
{
// ODF-tests
CHECK_EVAL("SLOPE(B4:B5;C4:C5)", Value(1)); //
CHECK_EVAL_SHORT("SLOPE(A19:A24;A26:A31)", Value(0.007813)); // TODO - be more precise /
}
void TestStatisticalFunctions::testSMALL()
{
// ODF-tests
CHECK_EVAL("SMALL(B14:B16;1)", Value(1)); //
CHECK_EVAL("SMALL(B14:B16;3)", Value(3)); //
CHECK_EVAL("SMALL(B14:B16;4)", Value::errorNUM()); // N is greater than the length of the list
}
void TestStatisticalFunctions::testSTANDARDIZE()
{
// ODF-tests
CHECK_EVAL("STANDARDIZE( 1; 2.5; 0.1 )", Value(-15)); //
CHECK_EVAL("STANDARDIZE( -1; -2; 2 )", Value(0.5)); //
CHECK_EVAL("STANDARDIZE( 1; 1; 0 )", Value::errorNUM()); // N is greater than the length of the list
}
void TestStatisticalFunctions::testSTDEV()
{
// ODF-tests
CHECK_EVAL("STDEV(2;4)/SQRT(2)", Value(1)); // The sample standard deviation of (2;4) is SQRT(2).
CHECK_EVAL("STDEV(B4:B5)*SQRT(2)", Value(1)); // The sample standard deviation of (2;3) is 1/SQRT(2).
CHECK_EVAL("STDEV(B3:B5)*SQRT(2)", Value(1)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("STDEV({10000000001;10000000002;"
"10000000003;10000000004;10000000005;"
"10000000006;10000000007;10000000008;"
"10000000009;10000000010})", Value(3.0276503541)); // Ensure that implementations use a reasonably stable way of calculating STDEV.
CHECK_EVAL("STDEV(1)", Value::errorNUM()); // At least two numbers must be included
}
void TestStatisticalFunctions::testSTDEVA()
{
// ODF-tests
CHECK_EVAL("STDEVA(2;4)/SQRT(2)", Value(1)); // The sample standard deviation of (2;4) is SQRT(2).
CHECK_EVAL_SHORT("STDEVA(B5:C6)", Value(2.581989)); // TODO - be more precise / Logicals (referenced) are converted to numbers.
CHECK_EVAL_SHORT("STDEVA( TRUE();FALSE() )", Value(0.707107)); // TODO - be more precise / Logicals (inlined) are converted to numbers.
CHECK_EVAL("STDEVA(1)", Value::errorNUM()); // Logicals (inlined) are converted to numbers.
}
void TestStatisticalFunctions::testSTDEVP()
{
// ODF-tests
CHECK_EVAL("STDEVP(2;4)", Value(1)); // The standard deviation of the set for (2;4) is 1.
CHECK_EVAL("STDEVP(B4:B5)*2", Value(1)); // The standard deviation of the set for (2;3) is 0.5.
CHECK_EVAL("STDEVP(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("STDEVP(1)", Value(0)); // STDEVP(1) is 0.
}
void TestStatisticalFunctions::testSTDEVPA()
{
// ODF-tests
CHECK_EVAL("STDEVPA(2;4)", Value(1)); // The sample standard deviation of (2;4) is 1.
CHECK_EVAL_SHORT("STDEVPA(B5:C6)", Value(2.236068)); // TODO - be more precise / Logicals (referenced) are converted to numbers.
CHECK_EVAL("STDEVPA(TRUE();FALSE())", Value(0.5)); // Logicals (inlined) are converted to numbers.
}
void TestStatisticalFunctions::testSTEYX()
{
// ODF-tests
CHECK_EVAL_SHORT("STEYX(C19:C23;A19:A23)", Value(2.370953)); // TODO - be more precise
CHECK_EVAL("STEYX(A19:A23;A25:A29)", Value(0)); //
CHECK_EVAL("STEYX(B4:B5;C4:C5)", Value::errorNUM()); // at least three number per sequence
}
void TestStatisticalFunctions::testSUMPRODUCT()
{
CHECK_EVAL("SUMPRODUCT(C19:C23;A19:A23)", Value(106));
CHECK_EVAL("SUMPRODUCT(C19:C23^2;2*A19:A23)", Value(820));
}
void TestStatisticalFunctions::testTDIST()
{
// mode
// 1 = one tailed distribution
// 2 = two tailed distribution
// ODF-tests
CHECK_EVAL("TDIST( 0.5; 1; 1 )", Value(0.3524163823)); // ODF-specs -> 0.352416
CHECK_EVAL_SHORT("TDIST( -1.5; 2; 2 )", Value(0.272393)); // TODO - be more precise / OOo-2.3.0 returns error!!!
CHECK_EVAL("TDIST( 0.5; 5; 1 )", Value(0.3191494358)); // ODF-specs -> 0.319149
CHECK_EVAL("TDIST( 1; 1; 3 )", Value::errorNUM()); // mode = { 1; 2 }
CHECK_EVAL("TDIST( 1; 0; 1 )", Value::errorNUM()); // degreeOfFreedom >= 1
}
void TestStatisticalFunctions::testTINV()
{
// TODO - be more precise
// ODF-tests
CHECK_EVAL("TINV( 1; 2 )", Value(0)); // p=1 -> t=0
CHECK_EVAL_SHORT("TINV( 0.5; 2 )", Value(0.816497)); //
CHECK_EVAL("TDIST( TINV(0.25;3); 3;2 )", Value(0.25)); //
CHECK_EVAL("TDIST( TINV(0.5 ;3); 3;2 )", Value(0.5)); //
CHECK_EVAL("TDIST( TINV(0.75;3); 3;2 )", Value(0.75)); //
CHECK_EVAL("TDIST( 2; 3 )", Value::errorNUM()); // 0 <= probability <= 1
CHECK_EVAL("TDIST( 1; 0 )", Value::errorNUM()); // degreeOfFreedom >= 1
}
void TestStatisticalFunctions::testTREND()
{
// Cell | Value Cell | Value
// ------+------ ------+------
// A19 | 1 C19 | 0
// A20 | 2 C20 | 5
// A21 | 4 C21 | 2
// A22 | 8 C22 | 5
// A23 | 16 C23 | 3
CHECK_ARRAY("TREND(A19:A23; C19:C23; 1) ", "{4.7555555555}"); // with offset
CHECK_ARRAY("TREND(A19:A23; C19:C23; 1; 0 ) ", "{1.6825396825}"); // without offset
}
void TestStatisticalFunctions::testTRIMMEAN()
{
// ODF-tests
CHECK_EVAL("TRIMMEAN(A19:A23; 0.8 )", Value(4)); // cutOff = 2
CHECK_EVAL("TRIMMEAN(A19:A23; 0.6 )", Value(4.6666666666)); // cutOff = FLOOR(5 * 0.6/ 2) = FLOOR(1.5) = 1;
// result = 14 / 3
CHECK_EVAL("TRIMMEAN(A19:A23; 0.19 )", Value(6.2)); // cutOff = 0
CHECK_EVAL("TRIMMEAN(A19:A23; 0.999999 )", Value(4)); // cutOff = 2
CHECK_EVAL("TRIMMEAN(A19:A23; 1)", Value::errorNUM()); // 0 <= cutOffFraction < 1
}
void TestStatisticalFunctions::testTTEST()
{
// ODF-tests
CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 1 )", Value(0.0427206184)); //
CHECK_EVAL("TTEST(A19:A23;A24:A28; 2; 1 )", Value(0.0854412368)); //
CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 2 )", Value(0.0294544970)); //
CHECK_EVAL("TTEST(A19:A23;A24:A28; 1; 3 )", Value(0.0462125526)); //
CHECK_EVAL("TTEST(A19:A23;A24:A29; 1; 1 )", Value::errorNUM()); // same amount of numbers for paired samples
CHECK_EVAL("TTEST(A19:A19;A24:A24; 1; 3 )", Value::errorNUM()); // two numbers at least for each sequence
}
void TestStatisticalFunctions::testVAR()
{
// ODF-tests
CHECK_EVAL("VAR(2;4)", Value(2)); // The sample variance of (2;4) is 2.
CHECK_EVAL("VAR(B4:B5)*2", Value(1)); // The sample variance of (2;3) is 0.5.
CHECK_EVAL("VAR(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("VAR(1)", Value::errorNUM()); // At least two numbers must be included
}
void TestStatisticalFunctions::testVARA()
{
// ODF-tests
CHECK_EVAL("VARA(2;4)", Value(2)); // The sample variance of (2;4) is 2.
CHECK_EVAL("VARA(B5:C6)", Value(6.6666666667)); // Logicals (referenced) are converted to numbers.
CHECK_EVAL("VARA(TRUE();FALSE())", Value(0.5)); // Logicals (inlined) are converted to numbers.
CHECK_EVAL("VARA(1)", Value::errorNUM()); // Two numbers at least.
}
void TestStatisticalFunctions::testVARIANCE()
{
// same as VAR
// ODF-tests
CHECK_EVAL("VARIANCE(2;4)", Value(2)); // The sample variance of (2;4) is 2.
CHECK_EVAL("VARIANCE(B4:B5)*2", Value(1)); // The sample variance of (2;3) is 0.5.
CHECK_EVAL("VARIANCE(B3:B5)*2", Value(1)); // Strings are not converted to numbers and are ignored.
CHECK_EVAL("VARIANCE(1)", Value::errorNUM()); // At least two numbers must be included
}
void TestStatisticalFunctions::testVARP()
{
// Cell | Value
// ------+-------
// B3 | "7"
// B4 | 2
// B5 | 3
// ODF-tests
CHECK_EVAL("VARP(2;4)", Value(1)); // The variance of the set for (2;4) is 1.
CHECK_EVAL("VARP(B4:B5)*4", Value(1)); // The variance of the set for (2;3) is 0.25.
CHECK_EVAL("VARP(B3:B5)*4", Value(1)); // Strings are not converted to numbers and are ignored.
}
void TestStatisticalFunctions::testVARPA()
{
// Cell | Value Cell | Value
// ------+------ ------+------
// B5 | 3 C5 | 5
// B6 | true C6 | 7
// ODF-tests
CHECK_EVAL("VARPA(2;4)", Value(1)); // The sample variance of (2;4) is 1.
CHECK_EVAL("VARPA(B5:C6)", Value(5)); // Logicals (referenced) are converted to numbers.
CHECK_EVAL("VARPA(TRUE();FALSE())", Value(0.25)); // Logicals (inlined) are converted to numbers.
}
void TestStatisticalFunctions::testWEIBULL()
{
// TODO - be more precise
// ODF-tests
CHECK_EVAL_SHORT("WEIBULL( 2; 3; 4; 0 )", Value(0.165468)); // pdf
CHECK_EVAL_SHORT("WEIBULL( 2; 3; 4; 1 )", Value(0.117503)); // cdf
CHECK_EVAL_SHORT("WEIBULL( -1; 3; 4; 0 )", Value::errorNUM()); // value >= 0
CHECK_EVAL_SHORT("WEIBULL( 2; 0; 4; 0 )", Value::errorNUM()); // alpha > 0
CHECK_EVAL_SHORT("WEIBULL( 2; 3; 0; 0 )", Value::errorNUM()); // beta > 0
}
void TestStatisticalFunctions::testZTEST()
{
// ODF-tests
CHECK_EVAL("ZTEST(B4:C5; 3.5 )", Value(0)); // mean = average, estimated standard deviation: fits well
CHECK_EVAL("ZTEST(B4:C5; 3 ; 2 )", Value(0.3829249225)); // mean near average, standard deviation greater than estimate: probable
CHECK_EVAL("ZTEST(B4:C5; 4 ; 0.5 )", Value(0.9544997361)); // mean near the average, but small deviation: not probable
CHECK_EVAL("ZTEST(B4:C5; 5 )", Value(0.9798632484)); // mean at a border value, standard deviation ~ 1,3: nearly improbable
CHECK_EVAL("ZTEST(B4:C5; 5 ; 0.1 )", Value(1)); // mean at a border value, small standard deviation: improbable
}
void TestStatisticalFunctions::cleanupTestCase()
{
delete m_map;
}
-QTEST_KDEMAIN(TestStatisticalFunctions, GUI)
+QTEST_MAIN(TestStatisticalFunctions)
diff --git a/sheets/tests/TestStatisticalFunctions.h b/sheets/tests/TestStatisticalFunctions.h
index a53047537f3..fcd5ae1aa87 100644
--- a/sheets/tests/TestStatisticalFunctions.h
+++ b/sheets/tests/TestStatisticalFunctions.h
@@ -1,153 +1,152 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_STATISTICAL_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_STATISTICAL_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class Map;
class TestStatisticalFunctions : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
// void testARRANG(); // alias PERMUT
void testAVEDEV();
void testAVERAGE();
void testAVERAGEA();
void testBETADIST();
void testBETAINV();
// void testBINO(); // kspread version of BINOMDIST with 3 Parameters
void testBINOMDIST();
void testCHIDIST();
// void testCOMBIN(); // in -> TestMathFunctions
// void testCOMBINA(); // in -> TestMathFunctions
void testCONFIDENCE();
void testCORREL();
void testCOVAR();
void testDEVSQ();
// void testDEVSQA(); // no ODF-test available
void testEXPONDIST();
void testFDIST();
void testFINV();
void testFISHER();
void testFISHERINV();
void testFREQUENCY();
void testFTEST();
void testGAMMADIST();
void testGAMMAINV();
// void testGAMMALN(); in -> TestMathFunctions
void testGAUSS();
void testGROWTH(); // to be implemented
void testGEOMEAN();
void testHARMEAN();
void testHYPGEOMDIST();
void testINTERCEPT();
// void testINVBINO();
void testKURT();
// void testKURTP(); // ???
void testLARGE();
void testLEGACYCHIDIST();
void testLEGACYCHIINV();
void testLEGACYFDIST();
void testLEGACYFINV();
// void testLEGACYNORMSDIST(); // same as NORMDIST required for OpenFormula compliance
// void testLINEST(); // ???
// void testLOGEST(); // ???
void testLOGINV();
void testLOGNORMDIST();
void testMAX();
void testMAXA();
void testMEDIAN();
void testMIN();
void testMINA();
void testMODE();
void testNEGBINOMDIST();
void testNORMDIST();
void testNORMINV();
// void testNORMSDIST();
// void testNORMSINV();
void testPEARSON();
void testPERCENTILE();
void testPERMUT();
void testPERMUTATIONA();
void testPHI();
void testPOISSON();
// void testPROB(); // ???
void testQUARTILE();
void testRANK();
void testRSQ();
void testSKEW();
void testSKEWP();
void testSLOPE();
void testSMALL();
void testSTANDARDIZE();
void testSTDEV();
void testSTDEVA();
void testSTDEVP();
void testSTDEVPA();
void testSTEYX();
// void testSUMXMY2(); // deprecated
void testSUMPRODUCT();
// void testSUMX2PY2();
// void testSUMX2MY2();
void testTDIST();
void testTINV();
void testTREND();
void testTRIMMEAN();
void testTTEST();
void testVAR();
void testVARA();
void testVARIANCE();
void testVARP();
void testVARPA();
void testWEIBULL();
void testZTEST();
void cleanupTestCase();
private:
bool TestArray(const QString& formula, const QString& Array, int accuracy, bool checkSize);
Value evaluate(const QString&);
Value TestDouble(const QString& formula, const Value& v2, int accuracy);
Map* m_map;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_STATISTICAL_FUNCTIONS
diff --git a/sheets/tests/TestStyleStorage.cpp b/sheets/tests/TestStyleStorage.cpp
index 79c96e24095..36f9ed18430 100644
--- a/sheets/tests/TestStyleStorage.cpp
+++ b/sheets/tests/TestStyleStorage.cpp
@@ -1,66 +1,66 @@
/* This file is part of the KDE project
Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestStyleStorage.h"
#include <StyleStorage.h>
#include <Map.h>
-#include <qtest_kde.h>
+#include <QTest>
using namespace Calligra::Sheets;
class MyStyleStorage : public StyleStorage
{
public:
MyStyleStorage(Map* map) : StyleStorage(map) {}
using StyleStorage::garbageCollection;
};
void TestStyleStorage::testGarbageCollection()
{
Map map;
MyStyleStorage storage(&map);
QRect rect(5, 5, 1, 1);
QColor c1(Qt::red);
QColor c2(Qt::blue);
SharedSubStyle style1(new SubStyleOne<Style::BackgroundColor, QColor>(c1));
SharedSubStyle style2(new SubStyleOne<Style::BackgroundColor, QColor>(c2));
// we need to do this for multiple cells, so hopefully we'll end up with substyles that are for the same cell but not in the same leafnode in the rtree
for (int i = 0; i < 100; i++)
storage.insert(rect.adjusted(10*i, 0, 10*i, 0), style1);
for (int i = 0; i < 100; i++)
QCOMPARE(storage.contains(rect.adjusted(10*i, 0, 10*i, 0)).backgroundColor(), c1);
for (int i = 0; i < 100; i++)
storage.insert(rect.adjusted(10*i, 0, 10*i, 0), style2);
for (int i = 0; i < 100; i++)
QCOMPARE(storage.contains(rect.adjusted(10*i, 0, 10*i, 0)).backgroundColor(), c2);
for (int i = 0; i < 100; i++)
storage.insert(rect.adjusted(10*i, 0, 10*i, 0), style1);
for (int i = 0; i < 100; i++)
QCOMPARE(storage.contains(rect.adjusted(10*i, 0, 10*i, 0)).backgroundColor(), c1);
for (int j = 0; j < 1000; j++) {
storage.garbageCollection();
for (int i = 0; i < 100; i++)
QCOMPARE(storage.contains(rect.adjusted(10*i, 0, 10*i, 0)).backgroundColor(), c1);
}
}
-QTEST_KDEMAIN(TestStyleStorage, GUI)
+QTEST_MAIN(TestStyleStorage)
diff --git a/sheets/tests/TestTextFunctions.cpp b/sheets/tests/TestTextFunctions.cpp
index 0b152d43542..95d5613ba03 100644
--- a/sheets/tests/TestTextFunctions.cpp
+++ b/sheets/tests/TestTextFunctions.cpp
@@ -1,299 +1,299 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestTextFunctions.h"
#include "TestKspreadCommon.h"
void TestTextFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
#define CHECK_EVAL(x,y) { Value z(y); QCOMPARE(evaluate(x,z),(z)); }
Value TestTextFunctions::evaluate(const QString& formula, Value& ex)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
if (result.isFloat() && ex.isInteger())
ex = Value(ex.asFloat());
if (result.isInteger() && ex.isFloat())
result = Value(result.asFloat());
return result;
}
void TestTextFunctions::testASC()
{
// TODO reactivate after function is implemented
// CHECK_EVAL( "ASC(\"ABC\")", Value( "ABC" ) );
// CHECK_EVAL( "ASC(\"アイウ\")", Value( "ァィゥ" ) );
}
void TestTextFunctions::testCHAR()
{
CHECK_EVAL("CHAR(65)", Value("A"));
CHECK_EVAL("CHAR(60)", Value("<"));
CHECK_EVAL("CHAR(97)", Value("a"));
CHECK_EVAL("CHAR(126)", Value("~"));
CHECK_EVAL("CHAR(32)", Value(" "));
// newline
CHECK_EVAL("LEN(CHAR(10))", Value(1));
// number has to be >=0
CHECK_EVAL("CHAR(-1)", Value::errorNUM());
}
void TestTextFunctions::testCLEAN()
{
CHECK_EVAL("CLEAN(\"Text\")", Value("Text"));
CHECK_EVAL("CLEAN(CHAR(7)&\"Tex\"&CHAR(8)&\"t\"&CHAR(9))", Value("Text"));
CHECK_EVAL("CLEAN(\"Hi there\")", Value("Hi there"));
}
void TestTextFunctions::testCODE()
{
CHECK_EVAL("CODE(\"A\")", Value(65));
CHECK_EVAL("CODE(\"0\")>0", Value(true));
CHECK_EVAL("CODE(\"Text\")=CODE(\"T\")", Value(true));
}
void TestTextFunctions::testCONCATENATE()
{
CHECK_EVAL("CONCATENATE(\"Hi \"; \"there\")", Value("Hi there"));
CHECK_EVAL("CONCATENATE(\"A\"; \"B\"; \"C\")", Value("ABC"));
CHECK_EVAL("CONCATENATE(2;3)", Value("23"));
CHECK_EVAL("CONCATENATE(23)", Value("23"));
}
void TestTextFunctions::testEXACT()
{
CHECK_EVAL("EXACT(\"A\";\"A\")", Value(true));
CHECK_EVAL("EXACT(\"A\";\"a\")", Value(false));
CHECK_EVAL("EXACT(1;1)", Value(true));
CHECK_EVAL("EXACT((1/3)*3;1)", Value(true));
CHECK_EVAL("EXACT(TRUE();TRUE())", Value(true));
CHECK_EVAL("EXACT(\"1\";2)", Value(false));
CHECK_EVAL("EXACT(\"h\";1)", Value(false));
CHECK_EVAL("EXACT(\"1\";1)", Value(true));
CHECK_EVAL("EXACT(\" 1\";1)", Value(false));
}
void TestTextFunctions::testFIND()
{
CHECK_EVAL("FIND(\"b\";\"abcabc\")", Value(2));
CHECK_EVAL("FIND(\"b\";\"abcabcabc\"; 3)", Value(5));
CHECK_EVAL("FIND(\"b\";\"ABC\";1)", Value::errorVALUE());
CHECK_EVAL("FIND(\"b\";\"bbbb\")", Value(1));
CHECK_EVAL("FIND(\"b\";\"bbbb\";2)", Value(2));
CHECK_EVAL("FIND(\"b\";\"bbbb\";2.9)", Value(2));
CHECK_EVAL("FIND(\"b\";\"bbbb\";0)", Value::errorVALUE());
CHECK_EVAL("FIND(\"b\";\"bbbb\";0.9)", Value::errorVALUE());
}
void TestTextFunctions::testFIXED()
{
CHECK_EVAL("FIXED(12345;3)", Value("12,345.000"));
CHECK_EVAL("ISTEXT(FIXED(12345;3))", Value(true));
CHECK_EVAL("FIXED(12345;3;FALSE())", Value("12,345.000"));
CHECK_EVAL("FIXED(12345;3.95;FALSE())", Value("12,345.000"));
CHECK_EVAL("FIXED(12345;4;TRUE())", Value("12345.0000"));
CHECK_EVAL("FIXED(123.45;1)", Value("123.5"));
CHECK_EVAL("FIXED(125.45; -1)", Value("130"));
CHECK_EVAL("FIXED(125.45; -1.1)", Value("130"));
CHECK_EVAL("FIXED(125.45; -1.9)", Value("130"));
CHECK_EVAL("FIXED(125.45; -2)", Value("100"));
CHECK_EVAL("FIXED(125.45; -2.87)", Value("100"));
CHECK_EVAL("FIXED(125.45; -3)", Value("0"));
CHECK_EVAL("FIXED(125.45; -4)", Value("0"));
CHECK_EVAL("FIXED(125.45; -5)", Value("0"));
}
void TestTextFunctions::testJIS()
{
// TODO reactivate after function is implemented
// CHECK_EVAL( "JIS(\"ABC\")", Value( "ABC") );
// CHECK_EVAL( "JIS(\"ァィゥ\")", Value( "アイウ" ) );
}
void TestTextFunctions::testLEFT()
{
CHECK_EVAL("LEFT(\"Hello\";2)", Value("He"));
CHECK_EVAL("LEFT(\"Hello\")", Value("H"));
CHECK_EVAL("LEFT(\"Hello\";20)", Value("Hello"));
CHECK_EVAL("LEFT(\"Hello\";0)", Value(""));
CHECK_EVAL("LEFT(\"\";4)", Value(""));
CHECK_EVAL("LEFT(\"xxx\";-0.1)", Value::errorVALUE());
CHECK_EVAL("LEFT(\"Hello\";2^15-1)", Value("Hello"));
CHECK_EVAL("LEFT(\"Hello\";2.9)", Value("He"));
}
void TestTextFunctions::testLEN()
{
CHECK_EVAL("LEN(\"Hi there\")", Value(8));
CHECK_EVAL("LEN(\"\")", Value(0));
CHECK_EVAL("LEN(55)", Value(2));
}
void TestTextFunctions::testLOWER()
{
CHECK_EVAL("LOWER(\"HELLObc7\")", Value("hellobc7"));
}
void TestTextFunctions::testMID()
{
CHECK_EVAL("MID(\"123456789\";5;3)", Value("567"));
CHECK_EVAL("MID(\"123456789\";20;3)", Value(""));
CHECK_EVAL("MID(\"123456789\";-1;0)", Value::errorVALUE());
CHECK_EVAL("MID(\"123456789\";1;0)", Value(""));
CHECK_EVAL("MID(\"123456789\";2.9;1)", Value("2"));
CHECK_EVAL("MID(\"123456789\";2;2.9)", Value("23"));
CHECK_EVAL("MID(\"123456789\";5)", Value("56789"));
}
void TestTextFunctions::testNUMBERVALUE()
{
CHECK_EVAL( "NUMBERVALUE(\"6\"; \".\")", Value( 6 ) ); // VALUE converts text to numbers (unlike N).
CHECK_EVAL( "NUMBERVALUE(\"6,000.5\"; \".\")", Value( 6000.5 ) ); // Period works.
CHECK_EVAL( "NUMBERVALUE(\"6.000,5\"; \",\")", Value( 6000.5 ) ); // Comma works
CHECK_EVAL( "NUMBERVALUE(\"3!456!000*567\"; \"*\"; \"!\")", Value( 3456000.567 ) ); // Thousands separator works
CHECK_EVAL( "NUMBERVALUE(\"+6,000.5\"; \".\")", Value( 6000.5 ) ); // Positive sign
CHECK_EVAL( "NUMBERVALUE(\"-6,000.5\"; \".\")", Value( -6000.5 ) ); // Negative sign
}
void TestTextFunctions::testPROPER()
{
CHECK_EVAL("PROPER(\"hello there\")", Value("Hello There"));
CHECK_EVAL("PROPER(\"HELLO THERE\")", Value("Hello There"));
CHECK_EVAL("PROPER(\"HELLO.THERE\")", Value("Hello.There"));
}
void TestTextFunctions::testREPLACE()
{
CHECK_EVAL("REPLACE(\"123456789\";5;3;\"Q\")", Value("1234Q89"));
CHECK_EVAL("REPLACE(\"123456789\";5;0;\"Q\")", Value("1234Q56789"));
}
void TestTextFunctions::testREPT()
{
CHECK_EVAL("REPT(\"X\";3)", Value("XXX"));
CHECK_EVAL("REPT(\"XY\";2)", Value("XYXY"));
CHECK_EVAL("REPT(\"X\";2.9)", Value("XX"));
CHECK_EVAL("REPT(\"XY\";2.9)", Value("XYXY"));
CHECK_EVAL("REPT(\"X\";0)", Value(""));
CHECK_EVAL("REPT(\"XYZ\";0)", Value(""));
CHECK_EVAL("REPT(\"X\";-1)", Value::errorVALUE());
CHECK_EVAL("REPT(\"XYZ\";-0.1)", Value::errorVALUE());
}
void TestTextFunctions::testRIGHT()
{
CHECK_EVAL("RIGHT(\"Hello\";2)", Value("lo"));
CHECK_EVAL("RIGHT(\"Hello\")", Value("o"));
CHECK_EVAL("RIGHT(\"Hello\";20)", Value("Hello"));
CHECK_EVAL("RIGHT(\"Hello\";0)", Value(""));
CHECK_EVAL("RIGHT(\"\";4)", Value(""));
CHECK_EVAL("RIGHT(\"xxx\";-1)", Value::errorVALUE());
CHECK_EVAL("RIGHT(\"xxx\";-0.1)", Value::errorVALUE());
CHECK_EVAL("RIGHT(\"Hello\";2^15-1)", Value("Hello"));
CHECK_EVAL("RIGHT(\"Hello\";2.9)", Value("lo"));
}
void TestTextFunctions::testSEARCH()
{
CHECK_EVAL("=SEARCH(\"b\";\"abcabc\")", Value(2));
CHECK_EVAL("=SEARCH(\"b\";\"abcabcabc\"; 3)", Value(5));
CHECK_EVAL("=SEARCH(\"b\";\"ABC\";1)", Value(2));
CHECK_EVAL("=SEARCH(\"c?a\";\"abcabcda\")", Value(6));
CHECK_EVAL("=SEARCH(\"e*o\";\"yes and no\")", Value(2));
CHECK_EVAL("=SEARCH(\"b*c\";\"abcabcabc\")", Value(2));
}
void TestTextFunctions::testSUBSTITUTE()
{
CHECK_EVAL("SUBSTITUTE(\"121212\";\"2\";\"ab\")", Value("1ab1ab1ab"));
CHECK_EVAL("SUBSTITUTE(\"121212\";\"2\";\"ab\";2)", Value("121ab12"));
CHECK_EVAL("SUBSTITUTE(\"Hello\";\"x\";\"ab\")", Value("Hello"));
CHECK_EVAL("SUBSTITUTE(\"xyz\";\"\";\"ab\")", Value("xyz"));
CHECK_EVAL("SUBSTITUTE(\"\";\"\";\"ab\")", Value(""));
CHECK_EVAL("SUBSTITUTE(\"Hello\"; \"H\"; \"J\"; 0)", Value::errorVALUE());
CHECK_EVAL("SUBSTITUTE(\"Hello\"; \"H\"; \"J\"; 1)", Value("Jello"));
CHECK_EVAL("SUBSTITUTE(\"fo\"\"o\";\"o\";\"a\")", Value("fa\"a"));
}
void TestTextFunctions::testT()
{
CHECK_EVAL("T(\"Hi\")", Value("Hi"));
CHECK_EVAL("T(5)", Value(""));
}
void TestTextFunctions::testTRIM()
{
CHECK_EVAL("TRIM(\" Hi \")", Value("Hi"));
CHECK_EVAL("LEN(TRIM(\"H\" & \" \" & \" \" & \"I\"))", Value(3));
}
void TestTextFunctions::testUNICHAR()
{
CHECK_EVAL("UNICHAR(65)", Value("A"));
CHECK_EVAL("UNICHAR(8364)", Value(QChar(8364)));
}
void TestTextFunctions::testUNICODE()
{
QChar euro(8364);
CHECK_EVAL("UNICODE(\"A\")", Value(65));
CHECK_EVAL("UNICODE(\"AB€C\")", Value(65));
CHECK_EVAL(QString("UNICODE(\"%1\")").arg(euro), Value(8364));
CHECK_EVAL(QString("UNICODE(\"%1F\")").arg(euro), Value(8364));
}
void TestTextFunctions::testUPPER()
{
CHECK_EVAL("UPPER(\"Habc7\")", Value("HABC7"));
}
void TestTextFunctions::testROT13()
{
CHECK_EVAL("ROT13(\"KSpread\")", Value("XFcernq"));
CHECK_EVAL("ROT13(\"XFcernq\")", Value("KSpread"));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13(\"KSpread\")", Value("XFcernq"));
CHECK_EVAL("COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13(\"XFcernq\")", Value("KSpread"));
}
void TestTextFunctions::testBAHTTEXT()
{
Value r;
r = evaluate("BAHTTEXT(23)", r);
CHECK_EVAL("BAHTTEXT(23)", r);
CHECK_EVAL("COM.MICROSOFT.BAHTTEXT(23)", r);
}
void TestTextFunctions::testTEXT()
{
CHECK_EVAL("TEXT(TIME(13;10;43);\"hh:mm\")", Value("13:10"));
}
-QTEST_KDEMAIN(TestTextFunctions, GUI)
+QTEST_MAIN(TestTextFunctions)
diff --git a/sheets/tests/TestTextFunctions.h b/sheets/tests/TestTextFunctions.h
index 8bec5c86cab..875e99482d2 100644
--- a/sheets/tests/TestTextFunctions.h
+++ b/sheets/tests/TestTextFunctions.h
@@ -1,74 +1,73 @@
/* This file is part of the KDE project
Copyright 2007 Brad Hards <bradh@frogmouth.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_TEXT_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_TEXT_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestTextFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testASC();
void testCHAR();
void testCLEAN();
void testCODE();
void testCONCATENATE();
void testEXACT();
void testFIND();
void testFIXED();
void testJIS();
void testLEFT();
void testLEN();
void testLOWER();
void testMID();
void testNUMBERVALUE();
void testPROPER();
void testREPLACE();
void testREPT();
void testRIGHT();
void testSEARCH();
void testSUBSTITUTE();
void testT();
void testTRIM();
void testUNICHAR();
void testUNICODE();
void testUPPER();
void testROT13();
void testBAHTTEXT();
void testTEXT();
private:
Value evaluate(const QString&, Value& ex);
};
} // namespace Sheets
} // namespace Calligra
#endif
diff --git a/sheets/tests/TestTrigFunctions.cpp b/sheets/tests/TestTrigFunctions.cpp
index 892cdb3faa7..a836c700e5f 100644
--- a/sheets/tests/TestTrigFunctions.cpp
+++ b/sheets/tests/TestTrigFunctions.cpp
@@ -1,661 +1,661 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestTrigFunctions.h"
#include "TestKspreadCommon.h"
void TestTrigFunctions::initTestCase()
{
FunctionModuleRegistry::instance()->loadFunctionModules();
}
// NOTE: we do not compare the numbers _exactly_ because it is difficult
// to get one "true correct" expected values for the functions due to:
// - different algorithms among spreadsheet programs
// - precision limitation of floating-point number representation
// - accuracy problem due to propagated error in the implementation
#define CHECK_EVAL(x,y) QCOMPARE(evaluate(x),RoundNumber(y))
#define ROUND(x) (roundf(1e10 * x) / 1e10)
// round to get at most 10-digits number
static Value RoundNumber(double f)
{
return Value(ROUND(f));
}
// round to get at most 10-digits number
static Value RoundNumber(const Value& v)
{
if (v.isNumber()) {
double d = numToDouble(v.asFloat());
if (fabs(d) < DBL_EPSILON)
d = 0.0;
return Value(ROUND(d));
} else
return v;
}
Value TestTrigFunctions::evaluate(const QString& formula)
{
Formula f;
QString expr = formula;
if (expr[0] != '=')
expr.prepend('=');
f.setExpression(expr);
Value result = f.eval();
#if 0
// this magically generates the CHECKs
printf(" CHECK_EVAL( \"%s\", %15g) );\n", qPrintable(formula), result.asFloat());
#endif
return RoundNumber(result);
}
void TestTrigFunctions::testCOS()
{
// some trivial cases
CHECK_EVAL("COS(0)", 1);
CHECK_EVAL("COS(PI()/2)", 0);
CHECK_EVAL("COS(PI())", -1);
CHECK_EVAL("COS(-PI()/2)", 0);
// 128 points in a circle
CHECK_EVAL("COS( 0*2*PI()/128 )", 1.000000000000000);
CHECK_EVAL("COS( 1*2*PI()/128 )", 0.998795456205172);
CHECK_EVAL("COS( 2*2*PI()/128 )", 0.995184726672197);
CHECK_EVAL("COS( 3*2*PI()/128 )", 0.989176509964781);
CHECK_EVAL("COS( 4*2*PI()/128 )", 0.980785280403230);
CHECK_EVAL("COS( 5*2*PI()/128 )", 0.970031253194544);
CHECK_EVAL("COS( 6*2*PI()/128 )", 0.956940335732209);
CHECK_EVAL("COS( 7*2*PI()/128 )", 0.941544065183021);
CHECK_EVAL("COS( 8*2*PI()/128 )", 0.923879532511287);
CHECK_EVAL("COS( 9*2*PI()/128 )", 0.903989293123443);
CHECK_EVAL("COS( 10*2*PI()/128 )", 0.881921264348355);
CHECK_EVAL("COS( 11*2*PI()/128 )", 0.857728610000272);
CHECK_EVAL("COS( 12*2*PI()/128 )", 0.831469612302545);
CHECK_EVAL("COS( 13*2*PI()/128 )", 0.803207531480645);
CHECK_EVAL("COS( 14*2*PI()/128 )", 0.773010453362737);
CHECK_EVAL("COS( 15*2*PI()/128 )", 0.740951125354959);
CHECK_EVAL("COS( 16*2*PI()/128 )", 0.707106781186548);
CHECK_EVAL("COS( 17*2*PI()/128 )", 0.671558954847018);
CHECK_EVAL("COS( 18*2*PI()/128 )", 0.634393284163645);
CHECK_EVAL("COS( 19*2*PI()/128 )", 0.595699304492433);
CHECK_EVAL("COS( 20*2*PI()/128 )", 0.555570233019602);
CHECK_EVAL("COS( 21*2*PI()/128 )", 0.514102744193222);
CHECK_EVAL("COS( 22*2*PI()/128 )", 0.471396736825998);
CHECK_EVAL("COS( 23*2*PI()/128 )", 0.427555093430282);
CHECK_EVAL("COS( 24*2*PI()/128 )", 0.382683432365090);
CHECK_EVAL("COS( 25*2*PI()/128 )", 0.336889853392220);
CHECK_EVAL("COS( 26*2*PI()/128 )", 0.290284677254462);
CHECK_EVAL("COS( 27*2*PI()/128 )", 0.242980179903264);
CHECK_EVAL("COS( 28*2*PI()/128 )", 0.195090322016128);
CHECK_EVAL("COS( 29*2*PI()/128 )", 0.146730474455362);
CHECK_EVAL("COS( 30*2*PI()/128 )", 0.0980171403295608);
CHECK_EVAL("COS( 31*2*PI()/128 )", 0.0490676743274181);
CHECK_EVAL("COS( 32*2*PI()/128 )", 0.000000000000000);
CHECK_EVAL("COS( 33*2*PI()/128 )", -0.0490676743274180);
CHECK_EVAL("COS( 34*2*PI()/128 )", -0.0980171403295606);
CHECK_EVAL("COS( 35*2*PI()/128 )", -0.146730474455362);
CHECK_EVAL("COS( 36*2*PI()/128 )", -0.195090322016128);
CHECK_EVAL("COS( 37*2*PI()/128 )", -0.242980179903264);
CHECK_EVAL("COS( 38*2*PI()/128 )", -0.290284677254462);
CHECK_EVAL("COS( 39*2*PI()/128 )", -0.336889853392220);
CHECK_EVAL("COS( 40*2*PI()/128 )", -0.382683432365090);
CHECK_EVAL("COS( 41*2*PI()/128 )", -0.427555093430282);
CHECK_EVAL("COS( 42*2*PI()/128 )", -0.471396736825998);
CHECK_EVAL("COS( 43*2*PI()/128 )", -0.514102744193222);
CHECK_EVAL("COS( 44*2*PI()/128 )", -0.555570233019602);
CHECK_EVAL("COS( 45*2*PI()/128 )", -0.595699304492433);
CHECK_EVAL("COS( 46*2*PI()/128 )", -0.634393284163645);
CHECK_EVAL("COS( 47*2*PI()/128 )", -0.671558954847018);
CHECK_EVAL("COS( 48*2*PI()/128 )", -0.707106781186547);
CHECK_EVAL("COS( 49*2*PI()/128 )", -0.740951125354959);
CHECK_EVAL("COS( 50*2*PI()/128 )", -0.773010453362737);
CHECK_EVAL("COS( 51*2*PI()/128 )", -0.803207531480645);
CHECK_EVAL("COS( 52*2*PI()/128 )", -0.831469612302545);
CHECK_EVAL("COS( 53*2*PI()/128 )", -0.857728610000272);
CHECK_EVAL("COS( 54*2*PI()/128 )", -0.881921264348355);
CHECK_EVAL("COS( 55*2*PI()/128 )", -0.903989293123443);
CHECK_EVAL("COS( 56*2*PI()/128 )", -0.923879532511287);
CHECK_EVAL("COS( 57*2*PI()/128 )", -0.941544065183021);
CHECK_EVAL("COS( 58*2*PI()/128 )", -0.956940335732209);
CHECK_EVAL("COS( 59*2*PI()/128 )", -0.970031253194544);
CHECK_EVAL("COS( 60*2*PI()/128 )", -0.980785280403230);
CHECK_EVAL("COS( 61*2*PI()/128 )", -0.989176509964781);
CHECK_EVAL("COS( 62*2*PI()/128 )", -0.995184726672197);
CHECK_EVAL("COS( 63*2*PI()/128 )", -0.998795456205172);
CHECK_EVAL("COS( 64*2*PI()/128 )", -1.000000000000000);
CHECK_EVAL("COS( 65*2*PI()/128 )", -0.998795456205172);
CHECK_EVAL("COS( 66*2*PI()/128 )", -0.995184726672197);
CHECK_EVAL("COS( 67*2*PI()/128 )", -0.989176509964781);
CHECK_EVAL("COS( 68*2*PI()/128 )", -0.980785280403230);
CHECK_EVAL("COS( 69*2*PI()/128 )", -0.970031253194544);
CHECK_EVAL("COS( 70*2*PI()/128 )", -0.956940335732209);
CHECK_EVAL("COS( 71*2*PI()/128 )", -0.941544065183021);
CHECK_EVAL("COS( 72*2*PI()/128 )", -0.923879532511287);
CHECK_EVAL("COS( 73*2*PI()/128 )", -0.903989293123443);
CHECK_EVAL("COS( 74*2*PI()/128 )", -0.881921264348355);
CHECK_EVAL("COS( 75*2*PI()/128 )", -0.857728610000272);
CHECK_EVAL("COS( 76*2*PI()/128 )", -0.831469612302545);
CHECK_EVAL("COS( 77*2*PI()/128 )", -0.803207531480645);
CHECK_EVAL("COS( 78*2*PI()/128 )", -0.773010453362737);
CHECK_EVAL("COS( 79*2*PI()/128 )", -0.740951125354959);
CHECK_EVAL("COS( 80*2*PI()/128 )", -0.707106781186548);
CHECK_EVAL("COS( 81*2*PI()/128 )", -0.671558954847019);
CHECK_EVAL("COS( 82*2*PI()/128 )", -0.634393284163646);
CHECK_EVAL("COS( 83*2*PI()/128 )", -0.595699304492433);
CHECK_EVAL("COS( 84*2*PI()/128 )", -0.555570233019602);
CHECK_EVAL("COS( 85*2*PI()/128 )", -0.514102744193222);
CHECK_EVAL("COS( 86*2*PI()/128 )", -0.471396736825998);
CHECK_EVAL("COS( 87*2*PI()/128 )", -0.427555093430282);
CHECK_EVAL("COS( 88*2*PI()/128 )", -0.382683432365090);
CHECK_EVAL("COS( 89*2*PI()/128 )", -0.336889853392220);
CHECK_EVAL("COS( 90*2*PI()/128 )", -0.290284677254462);
CHECK_EVAL("COS( 91*2*PI()/128 )", -0.242980179903264);
CHECK_EVAL("COS( 92*2*PI()/128 )", -0.195090322016129);
CHECK_EVAL("COS( 93*2*PI()/128 )", -0.146730474455362);
CHECK_EVAL("COS( 94*2*PI()/128 )", -0.0980171403295605);
CHECK_EVAL("COS( 95*2*PI()/128 )", -0.0490676743274180);
CHECK_EVAL("COS( 96*2*PI()/128 )", -0.000000000000000);
CHECK_EVAL("COS( 97*2*PI()/128 )", 0.0490676743274177);
CHECK_EVAL("COS( 98*2*PI()/128 )", 0.0980171403295601);
CHECK_EVAL("COS( 99*2*PI()/128 )", 0.146730474455362);
CHECK_EVAL("COS( 100*2*PI()/128 )", 0.195090322016128);
CHECK_EVAL("COS( 101*2*PI()/128 )", 0.242980179903264);
CHECK_EVAL("COS( 102*2*PI()/128 )", 0.290284677254462);
CHECK_EVAL("COS( 103*2*PI()/128 )", 0.336889853392220);
CHECK_EVAL("COS( 104*2*PI()/128 )", 0.382683432365090);
CHECK_EVAL("COS( 105*2*PI()/128 )", 0.427555093430282);
CHECK_EVAL("COS( 106*2*PI()/128 )", 0.471396736825998);
CHECK_EVAL("COS( 107*2*PI()/128 )", 0.514102744193222);
CHECK_EVAL("COS( 108*2*PI()/128 )", 0.555570233019602);
CHECK_EVAL("COS( 109*2*PI()/128 )", 0.595699304492433);
CHECK_EVAL("COS( 110*2*PI()/128 )", 0.634393284163646);
CHECK_EVAL("COS( 111*2*PI()/128 )", 0.671558954847018);
CHECK_EVAL("COS( 112*2*PI()/128 )", 0.707106781186547);
CHECK_EVAL("COS( 113*2*PI()/128 )", 0.740951125354959);
CHECK_EVAL("COS( 114*2*PI()/128 )", 0.773010453362737);
CHECK_EVAL("COS( 115*2*PI()/128 )", 0.803207531480645);
CHECK_EVAL("COS( 116*2*PI()/128 )", 0.831469612302545);
CHECK_EVAL("COS( 117*2*PI()/128 )", 0.857728610000272);
CHECK_EVAL("COS( 118*2*PI()/128 )", 0.881921264348355);
CHECK_EVAL("COS( 119*2*PI()/128 )", 0.903989293123443);
CHECK_EVAL("COS( 120*2*PI()/128 )", 0.923879532511287);
CHECK_EVAL("COS( 121*2*PI()/128 )", 0.941544065183021);
CHECK_EVAL("COS( 122*2*PI()/128 )", 0.956940335732209);
CHECK_EVAL("COS( 123*2*PI()/128 )", 0.970031253194544);
CHECK_EVAL("COS( 124*2*PI()/128 )", 0.980785280403230);
CHECK_EVAL("COS( 125*2*PI()/128 )", 0.989176509964781);
CHECK_EVAL("COS( 126*2*PI()/128 )", 0.995184726672197);
CHECK_EVAL("COS( 127*2*PI()/128 )", 0.998795456205172);
// Cosinus needs to be a numeric value
CHECK_EVAL("COS(raghu)", Value::errorVALUE());
CHECK_EVAL("COS()", Value::errorVALUE());
CHECK_EVAL("1+COS(0.2)", Value(1.980066578));
}
// hyperbolic cosine
void TestTrigFunctions::testCOSH()
{
CHECK_EVAL("COSH( 0*PI()/64 )", 1.00000000000000);
CHECK_EVAL("COSH( 1*PI()/64 )", 1.00120502763102);
CHECK_EVAL("COSH( 2*PI()/64 )", 1.00482301470726);
CHECK_EVAL("COSH( 3*PI()/64 )", 1.01086268077751);
CHECK_EVAL("COSH( 4*PI()/64 )", 1.01933858177076);
CHECK_EVAL("COSH( 5*PI()/64 )", 1.03027114507681);
CHECK_EVAL("COSH( 6*PI()/64 )", 1.04368671877737);
CHECK_EVAL("COSH( 7*PI()/64 )", 1.05961763514644);
CHECK_EVAL("COSH( 8*PI()/64 )", 1.07810228857284);
CHECK_EVAL("COSH( 9*PI()/64 )", 1.09918522809284);
CHECK_EVAL("COSH( 10*PI()/64 )", 1.12291726475574);
CHECK_EVAL("COSH( 11*PI()/64 )", 1.14935559408141);
CHECK_EVAL("COSH( 12*PI()/64 )", 1.17856393390454);
CHECK_EVAL("COSH( 13*PI()/64 )", 1.21061267793823);
CHECK_EVAL("COSH( 14*PI()/64 )", 1.24557906542667);
CHECK_EVAL("COSH( 15*PI()/64 )", 1.28354736729603);
CHECK_EVAL("COSH( 16*PI()/64 )", 1.32460908925201);
CHECK_EVAL("COSH( 17*PI()/64 )", 1.36886319231368);
CHECK_EVAL("COSH( 18*PI()/64 )", 1.41641633131500);
CHECK_EVAL("COSH( 19*PI()/64 )", 1.46738311194883);
CHECK_EVAL("COSH( 20*PI()/64 )", 1.52188636697305);
CHECK_EVAL("COSH( 21*PI()/64 )", 1.58005745224421);
CHECK_EVAL("COSH( 22*PI()/64 )", 1.64203656329247);
CHECK_EVAL("COSH( 23*PI()/64 )", 1.70797307320055);
CHECK_EVAL("COSH( 24*PI()/64 )", 1.77802589260111);
CHECK_EVAL("COSH( 25*PI()/64 )", 1.85236385266018);
CHECK_EVAL("COSH( 26*PI()/64 )", 1.93116611196955);
CHECK_EVAL("COSH( 27*PI()/64 )", 2.01462258832895);
CHECK_EVAL("COSH( 28*PI()/64 )", 2.10293441645836);
CHECK_EVAL("COSH( 29*PI()/64 )", 2.19631443274388);
CHECK_EVAL("COSH( 30*PI()/64 )", 2.29498768818512);
CHECK_EVAL("COSH( 31*PI()/64 )", 2.39919199078058);
CHECK_EVAL("COSH( 32*PI()/64 )", 2.50917847865806);
CHECK_EVAL("COSH( 33*PI()/64 )", 2.62521222533141);
CHECK_EVAL("COSH( 34*PI()/64 )", 2.74757287854239);
CHECK_EVAL("COSH( 35*PI()/64 )", 2.87655533422713);
CHECK_EVAL("COSH( 36*PI()/64 )", 3.01247044723167);
CHECK_EVAL("COSH( 37*PI()/64 )", 3.15564578048928);
CHECK_EVAL("COSH( 38*PI()/64 )", 3.30642639446529);
CHECK_EVAL("COSH( 39*PI()/64 )", 3.46517567877181);
CHECK_EVAL("COSH( 40*PI()/64 )", 3.63227622795684);
CHECK_EVAL("COSH( 41*PI()/64 )", 3.80813076357823);
CHECK_EVAL("COSH( 42*PI()/64 )", 3.99316310478491);
CHECK_EVAL("COSH( 43*PI()/64 )", 4.18781918974444);
CHECK_EVAL("COSH( 44*PI()/64 )", 4.39256815037867);
CHECK_EVAL("COSH( 45*PI()/64 )", 4.60790344299758);
CHECK_EVAL("COSH( 46*PI()/64 )", 4.83434403755624);
CHECK_EVAL("COSH( 47*PI()/64 )", 5.07243566840111);
CHECK_EVAL("COSH( 48*PI()/64 )", 5.32275214951996);
CHECK_EVAL("COSH( 49*PI()/64 )", 5.58589675746527);
CHECK_EVAL("COSH( 50*PI()/64 )", 5.86250368528411);
CHECK_EVAL("COSH( 51*PI()/64 )", 6.15323957095837);
CHECK_EVAL("COSH( 52*PI()/64 )", 6.45880510403919);
CHECK_EVAL("COSH( 53*PI()/64 )", 6.77993671434747);
CHECK_EVAL("COSH( 54*PI()/64 )", 7.11740834681045);
CHECK_EVAL("COSH( 55*PI()/64 )", 7.47203332671172);
CHECK_EVAL("COSH( 56*PI()/64 )", 7.84466631985014);
CHECK_EVAL("COSH( 57*PI()/64 )", 8.23620539233163);
CHECK_EVAL("COSH( 58*PI()/64 )", 8.64759417495814);
CHECK_EVAL("COSH( 59*PI()/64 )", 9.07982413742996);
CHECK_EVAL("COSH( 60*PI()/64 )", 9.53393697784256);
CHECK_EVAL("COSH( 61*PI()/64 )", 10.0110271332365);
CHECK_EVAL("COSH( 62*PI()/64 )", 10.5122444172514);
CHECK_EVAL("COSH( 63*PI()/64 )", 11.0387967912398);
}
void TestTrigFunctions::testPI()
{
CHECK_EVAL("PI()", 3.14159265358979);
CHECK_EVAL("2*PI()", 6.28318530717959);
CHECK_EVAL("3*PI()", 9.42477796076938);
CHECK_EVAL("PI()/2", 1.57079632679490);
CHECK_EVAL("PI()/PI()", 1.0);
CHECK_EVAL("PI()/(2*PI())", 0.5);
CHECK_EVAL("(2*PI())/(2*PI())", 1.0);
}
void TestTrigFunctions::testSIN()
{
// some trivial cases
CHECK_EVAL("SIN(0)", 0);
CHECK_EVAL("SIN(PI()/2)", 1);
CHECK_EVAL("SIN(PI())", 0);
CHECK_EVAL("SIN(-PI()/2)", -1);
// 128 points in a circle
CHECK_EVAL("SIN( 0*2*PI()/128 )", 0.000000000000000);
CHECK_EVAL("SIN( 1*2*PI()/128 )", 0.049067674327418);
CHECK_EVAL("SIN( 2*2*PI()/128 )", 0.0980171403295606);
CHECK_EVAL("SIN( 3*2*PI()/128 )", 0.146730474455362);
CHECK_EVAL("SIN( 4*2*PI()/128 )", 0.195090322016128);
CHECK_EVAL("SIN( 5*2*PI()/128 )", 0.242980179903264);
CHECK_EVAL("SIN( 6*2*PI()/128 )", 0.290284677254462);
CHECK_EVAL("SIN( 7*2*PI()/128 )", 0.336889853392220);
CHECK_EVAL("SIN( 8*2*PI()/128 )", 0.382683432365090);
CHECK_EVAL("SIN( 9*2*PI()/128 )", 0.427555093430282);
CHECK_EVAL("SIN( 10*2*PI()/128 )", 0.471396736825998);
CHECK_EVAL("SIN( 11*2*PI()/128 )", 0.514102744193222);
CHECK_EVAL("SIN( 12*2*PI()/128 )", 0.555570233019602);
CHECK_EVAL("SIN( 13*2*PI()/128 )", 0.595699304492433);
CHECK_EVAL("SIN( 14*2*PI()/128 )", 0.634393284163645);
CHECK_EVAL("SIN( 15*2*PI()/128 )", 0.671558954847018);
CHECK_EVAL("SIN( 16*2*PI()/128 )", 0.707106781186547);
CHECK_EVAL("SIN( 17*2*PI()/128 )", 0.740951125354959);
CHECK_EVAL("SIN( 18*2*PI()/128 )", 0.773010453362737);
CHECK_EVAL("SIN( 19*2*PI()/128 )", 0.803207531480645);
CHECK_EVAL("SIN( 20*2*PI()/128 )", 0.831469612302545);
CHECK_EVAL("SIN( 21*2*PI()/128 )", 0.857728610000272);
CHECK_EVAL("SIN( 22*2*PI()/128 )", 0.881921264348355);
CHECK_EVAL("SIN( 23*2*PI()/128 )", 0.903989293123443);
CHECK_EVAL("SIN( 24*2*PI()/128 )", 0.923879532511287);
CHECK_EVAL("SIN( 25*2*PI()/128 )", 0.941544065183021);
CHECK_EVAL("SIN( 26*2*PI()/128 )", 0.956940335732209);
CHECK_EVAL("SIN( 27*2*PI()/128 )", 0.970031253194544);
CHECK_EVAL("SIN( 28*2*PI()/128 )", 0.980785280403230);
CHECK_EVAL("SIN( 29*2*PI()/128 )", 0.989176509964781);
CHECK_EVAL("SIN( 30*2*PI()/128 )", 0.995184726672197);
CHECK_EVAL("SIN( 31*2*PI()/128 )", 0.998795456205172);
CHECK_EVAL("SIN( 32*2*PI()/128 )", 1.000000000000000);
CHECK_EVAL("SIN( 33*2*PI()/128 )", 0.998795456205172);
CHECK_EVAL("SIN( 34*2*PI()/128 )", 0.995184726672197);
CHECK_EVAL("SIN( 35*2*PI()/128 )", 0.989176509964781);
CHECK_EVAL("SIN( 36*2*PI()/128 )", 0.980785280403230);
CHECK_EVAL("SIN( 37*2*PI()/128 )", 0.970031253194544);
CHECK_EVAL("SIN( 38*2*PI()/128 )", 0.956940335732209);
CHECK_EVAL("SIN( 39*2*PI()/128 )", 0.941544065183021);
CHECK_EVAL("SIN( 40*2*PI()/128 )", 0.923879532511287);
CHECK_EVAL("SIN( 41*2*PI()/128 )", 0.903989293123443);
CHECK_EVAL("SIN( 42*2*PI()/128 )", 0.881921264348355);
CHECK_EVAL("SIN( 43*2*PI()/128 )", 0.857728610000272);
CHECK_EVAL("SIN( 44*2*PI()/128 )", 0.831469612302545);
CHECK_EVAL("SIN( 45*2*PI()/128 )", 0.803207531480645);
CHECK_EVAL("SIN( 46*2*PI()/128 )", 0.773010453362737);
CHECK_EVAL("SIN( 47*2*PI()/128 )", 0.740951125354959);
CHECK_EVAL("SIN( 48*2*PI()/128 )", 0.707106781186548);
CHECK_EVAL("SIN( 49*2*PI()/128 )", 0.671558954847019);
CHECK_EVAL("SIN( 50*2*PI()/128 )", 0.634393284163645);
CHECK_EVAL("SIN( 51*2*PI()/128 )", 0.595699304492433);
CHECK_EVAL("SIN( 52*2*PI()/128 )", 0.555570233019602);
CHECK_EVAL("SIN( 53*2*PI()/128 )", 0.514102744193222);
CHECK_EVAL("SIN( 54*2*PI()/128 )", 0.471396736825998);
CHECK_EVAL("SIN( 55*2*PI()/128 )", 0.427555093430282);
CHECK_EVAL("SIN( 56*2*PI()/128 )", 0.382683432365090);
CHECK_EVAL("SIN( 57*2*PI()/128 )", 0.336889853392220);
CHECK_EVAL("SIN( 58*2*PI()/128 )", 0.290284677254462);
CHECK_EVAL("SIN( 59*2*PI()/128 )", 0.242980179903264);
CHECK_EVAL("SIN( 60*2*PI()/128 )", 0.195090322016129);
CHECK_EVAL("SIN( 61*2*PI()/128 )", 0.146730474455362);
CHECK_EVAL("SIN( 62*2*PI()/128 )", 0.0980171403295608);
CHECK_EVAL("SIN( 63*2*PI()/128 )", 0.049067674327418);
CHECK_EVAL("SIN( 64*2*PI()/128 )", 0.000000000000000);
CHECK_EVAL("SIN( 65*2*PI()/128 )", -0.0490676743274177);
CHECK_EVAL("SIN( 66*2*PI()/128 )", -0.0980171403295606);
CHECK_EVAL("SIN( 67*2*PI()/128 )", -0.146730474455362);
CHECK_EVAL("SIN( 68*2*PI()/128 )", -0.195090322016128);
CHECK_EVAL("SIN( 69*2*PI()/128 )", -0.242980179903264);
CHECK_EVAL("SIN( 70*2*PI()/128 )", -0.290284677254462);
CHECK_EVAL("SIN( 71*2*PI()/128 )", -0.336889853392220);
CHECK_EVAL("SIN( 72*2*PI()/128 )", -0.382683432365090);
CHECK_EVAL("SIN( 73*2*PI()/128 )", -0.427555093430282);
CHECK_EVAL("SIN( 74*2*PI()/128 )", -0.471396736825998);
CHECK_EVAL("SIN( 75*2*PI()/128 )", -0.514102744193222);
CHECK_EVAL("SIN( 76*2*PI()/128 )", -0.555570233019602);
CHECK_EVAL("SIN( 77*2*PI()/128 )", -0.595699304492433);
CHECK_EVAL("SIN( 78*2*PI()/128 )", -0.634393284163645);
CHECK_EVAL("SIN( 79*2*PI()/128 )", -0.671558954847018);
CHECK_EVAL("SIN( 80*2*PI()/128 )", -0.707106781186547);
CHECK_EVAL("SIN( 81*2*PI()/128 )", -0.740951125354959);
CHECK_EVAL("SIN( 82*2*PI()/128 )", -0.773010453362737);
CHECK_EVAL("SIN( 83*2*PI()/128 )", -0.803207531480645);
CHECK_EVAL("SIN( 84*2*PI()/128 )", -0.831469612302545);
CHECK_EVAL("SIN( 85*2*PI()/128 )", -0.857728610000272);
CHECK_EVAL("SIN( 86*2*PI()/128 )", -0.881921264348355);
CHECK_EVAL("SIN( 87*2*PI()/128 )", -0.903989293123443);
CHECK_EVAL("SIN( 88*2*PI()/128 )", -0.923879532511287);
CHECK_EVAL("SIN( 89*2*PI()/128 )", -0.941544065183021);
CHECK_EVAL("SIN( 90*2*PI()/128 )", -0.956940335732209);
CHECK_EVAL("SIN( 91*2*PI()/128 )", -0.970031253194544);
CHECK_EVAL("SIN( 92*2*PI()/128 )", -0.980785280403230);
CHECK_EVAL("SIN( 93*2*PI()/128 )", -0.989176509964781);
CHECK_EVAL("SIN( 94*2*PI()/128 )", -0.995184726672197);
CHECK_EVAL("SIN( 95*2*PI()/128 )", -0.998795456205172);
CHECK_EVAL("SIN( 96*2*PI()/128 )", -1.000000000000000);
CHECK_EVAL("SIN( 97*2*PI()/128 )", -0.998795456205172);
CHECK_EVAL("SIN( 98*2*PI()/128 )", -0.995184726672197);
CHECK_EVAL("SIN( 99*2*PI()/128 )", -0.989176509964781);
CHECK_EVAL("SIN( 100*2*PI()/128 )", -0.980785280403230);
CHECK_EVAL("SIN( 101*2*PI()/128 )", -0.970031253194544);
CHECK_EVAL("SIN( 102*2*PI()/128 )", -0.956940335732209);
CHECK_EVAL("SIN( 103*2*PI()/128 )", -0.941544065183021);
CHECK_EVAL("SIN( 104*2*PI()/128 )", -0.923879532511287);
CHECK_EVAL("SIN( 105*2*PI()/128 )", -0.903989293123443);
CHECK_EVAL("SIN( 106*2*PI()/128 )", -0.881921264348355);
CHECK_EVAL("SIN( 107*2*PI()/128 )", -0.857728610000272);
CHECK_EVAL("SIN( 108*2*PI()/128 )", -0.831469612302545);
CHECK_EVAL("SIN( 109*2*PI()/128 )", -0.803207531480645);
CHECK_EVAL("SIN( 110*2*PI()/128 )", -0.773010453362737);
CHECK_EVAL("SIN( 111*2*PI()/128 )", -0.740951125354959);
CHECK_EVAL("SIN( 112*2*PI()/128 )", -0.707106781186548);
CHECK_EVAL("SIN( 113*2*PI()/128 )", -0.671558954847019);
CHECK_EVAL("SIN( 114*2*PI()/128 )", -0.634393284163646);
CHECK_EVAL("SIN( 115*2*PI()/128 )", -0.595699304492433);
CHECK_EVAL("SIN( 116*2*PI()/128 )", -0.555570233019602);
CHECK_EVAL("SIN( 117*2*PI()/128 )", -0.514102744193222);
CHECK_EVAL("SIN( 118*2*PI()/128 )", -0.471396736825998);
CHECK_EVAL("SIN( 119*2*PI()/128 )", -0.427555093430283);
CHECK_EVAL("SIN( 120*2*PI()/128 )", -0.382683432365090);
CHECK_EVAL("SIN( 121*2*PI()/128 )", -0.336889853392220);
CHECK_EVAL("SIN( 122*2*PI()/128 )", -0.290284677254462);
CHECK_EVAL("SIN( 123*2*PI()/128 )", -0.242980179903264);
CHECK_EVAL("SIN( 124*2*PI()/128 )", -0.195090322016129);
CHECK_EVAL("SIN( 125*2*PI()/128 )", -0.146730474455362);
CHECK_EVAL("SIN( 126*2*PI()/128 )", -0.0980171403295605);
CHECK_EVAL("SIN( 127*2*PI()/128 )", -0.0490676743274181);
}
// hyperbolic sine
void TestTrigFunctions::testSINH()
{
CHECK_EVAL("SINH( 0*PI()/64 )", 0.00000000000000);
CHECK_EVAL("SINH( 1*PI()/64 )", 0.0491071008473137);
CHECK_EVAL("SINH( 2*PI()/64 )", 0.0983325525214279);
CHECK_EVAL("SINH( 3*PI()/64 )", 0.147794991081176);
CHECK_EVAL("SINH( 4*PI()/64 )", 0.197613623736882);
CHECK_EVAL("SINH( 5*PI()/64 )", 0.247908516146325);
CHECK_EVAL("SINH( 6*PI()/64 )", 0.298800881779610);
CHECK_EVAL("SINH( 7*PI()/64 )", 0.350413374050329);
CHECK_EVAL("SINH( 8*PI()/64 )", 0.402870381917066);
CHECK_EVAL("SINH( 9*PI()/64 )", 0.456298329667662);
CHECK_EVAL("SINH( 10*PI()/64 )", 0.510825981608731);
CHECK_EVAL("SINH( 11*PI()/64 )", 0.566584752394762);
CHECK_EVAL("SINH( 12*PI()/64 )", 0.623709023744691);
CHECK_EVAL("SINH( 13*PI()/64 )", 0.682336468309276);
CHECK_EVAL("SINH( 14*PI()/64 )", 0.742608381469790);
CHECK_EVAL("SINH( 15*PI()/64 )", 0.804670021867697);
CHECK_EVAL("SINH( 16*PI()/64 )", 0.868670961486010);
CHECK_EVAL("SINH( 17*PI()/64 )", 0.934765446126030);
CHECK_EVAL("SINH( 18*PI()/64 )", 1.00311276714826);
CHECK_EVAL("SINH( 19*PI()/64 )", 1.07387764537337);
CHECK_EVAL("SINH( 20*PI()/64 )", 1.14723062806849);
CHECK_EVAL("SINH( 21*PI()/64 )", 1.22334849997556);
CHECK_EVAL("SINH( 22*PI()/64 )", 1.30241470937230);
CHECK_EVAL("SINH( 23*PI()/64 )", 1.38461981019272);
CHECK_EVAL("SINH( 24*PI()/64 )", 1.47016192127261);
CHECK_EVAL("SINH( 25*PI()/64 )", 1.55924720382691);
CHECK_EVAL("SINH( 26*PI()/64 )", 1.65209035830962);
CHECK_EVAL("SINH( 27*PI()/64 )", 1.74891514185372);
CHECK_EVAL("SINH( 28*PI()/64 )", 1.84995490753831);
CHECK_EVAL("SINH( 29*PI()/64 )", 1.95545316678234);
CHECK_EVAL("SINH( 30*PI()/64 )", 2.06566417622064);
CHECK_EVAL("SINH( 31*PI()/64 )", 2.18085355047644);
CHECK_EVAL("SINH( 32*PI()/64 )", 2.30129890230729);
CHECK_EVAL("SINH( 33*PI()/64 )", 2.42729051166718);
CHECK_EVAL("SINH( 34*PI()/64 )", 2.55913202529720);
CHECK_EVAL("SINH( 35*PI()/64 )", 2.69714118853103);
CHECK_EVAL("SINH( 36*PI()/64 )", 2.84165061107874);
CHECK_EVAL("SINH( 37*PI()/64 )", 2.99300856863454);
CHECK_EVAL("SINH( 38*PI()/64 )", 3.15157984224051);
CHECK_EVAL("SINH( 39*PI()/64 )", 3.31774659742900);
CHECK_EVAL("SINH( 40*PI()/64 )", 3.49190930526272);
CHECK_EVAL("SINH( 41*PI()/64 )", 3.67448770749215);
CHECK_EVAL("SINH( 42*PI()/64 )", 3.86592182815631);
CHECK_EVAL("SINH( 43*PI()/64 )", 4.06667303406504);
CHECK_EVAL("SINH( 44*PI()/64 )", 4.27722514671850);
CHECK_EVAL("SINH( 45*PI()/64 )", 4.49808560834373);
CHECK_EVAL("SINH( 46*PI()/64 )", 4.72978670485843);
CHECK_EVAL("SINH( 47*PI()/64 )", 4.97288684870950);
CHECK_EVAL("SINH( 48*PI()/64 )", 5.22797192467780);
CHECK_EVAL("SINH( 49*PI()/64 )", 5.49565670189296);
CHECK_EVAL("SINH( 50*PI()/64 )", 5.77658631546086);
CHECK_EVAL("SINH( 51*PI()/64 )", 6.07143782127495);
CHECK_EVAL("SINH( 52*PI()/64 )", 6.38092182775833);
CHECK_EVAL("SINH( 53*PI()/64 )", 6.70578420846934);
CHECK_EVAL("SINH( 54*PI()/64 )", 7.04680789969806);
CHECK_EVAL("SINH( 55*PI()/64 )", 7.40481478738601);
CHECK_EVAL("SINH( 56*PI()/64 )", 7.78066768791671);
CHECK_EVAL("SINH( 57*PI()/64 )", 8.17527242755082);
CHECK_EVAL("SINH( 58*PI()/64 )", 8.58958002551754);
CHECK_EVAL("SINH( 59*PI()/64 )", 9.02458898602345);
CHECK_EVAL("SINH( 60*PI()/64 )", 9.48134770470283);
CHECK_EVAL("SINH( 61*PI()/64 )", 9.96095699530914);
CHECK_EVAL("SINH( 62*PI()/64 )", 10.4645727427369);
CHECK_EVAL("SINH( 63*PI()/64 )", 10.9934086887683);
}
void TestTrigFunctions::testTAN()
{
// some trivial cases
CHECK_EVAL("TAN(0)", 0);
CHECK_EVAL("TAN(PI()/4)", 1);
CHECK_EVAL("TAN(PI())", 0);
CHECK_EVAL("TAN(-PI()/4)", -1);
// 128 points in a circle, except where the result is infinity
CHECK_EVAL("TAN( 0*2*PI()/128 )", 0.000000000000000);
CHECK_EVAL("TAN( 1*2*PI()/128 )", 0.0491268497694673);
CHECK_EVAL("TAN( 2*2*PI()/128 )", 0.0984914033571642);
CHECK_EVAL("TAN( 3*2*PI()/128 )", 0.148335987538347);
CHECK_EVAL("TAN( 4*2*PI()/128 )", 0.198912367379658);
CHECK_EVAL("TAN( 5*2*PI()/128 )", 0.250486960191305);
CHECK_EVAL("TAN( 6*2*PI()/128 )", 0.303346683607342);
CHECK_EVAL("TAN( 7*2*PI()/128 )", 0.357805721314524);
CHECK_EVAL("TAN( 8*2*PI()/128 )", 0.414213562373095);
CHECK_EVAL("TAN( 9*2*PI()/128 )", 0.472964775891320);
CHECK_EVAL("TAN( 10*2*PI()/128 )", 0.534511135950792);
CHECK_EVAL("TAN( 11*2*PI()/128 )", 0.599376933681924);
CHECK_EVAL("TAN( 12*2*PI()/128 )", 0.668178637919299);
CHECK_EVAL("TAN( 13*2*PI()/128 )", 0.741650546272035);
CHECK_EVAL("TAN( 14*2*PI()/128 )", 0.820678790828660);
CHECK_EVAL("TAN( 15*2*PI()/128 )", 0.906347169019147);
CHECK_EVAL("TAN( 16*2*PI()/128 )", 1.000000000000000);
CHECK_EVAL("TAN( 17*2*PI()/128 )", 1.103329975733476);
CHECK_EVAL("TAN( 18*2*PI()/128 )", 1.218503525587976);
CHECK_EVAL("TAN( 19*2*PI()/128 )", 1.348343913486720);
CHECK_EVAL("TAN( 20*2*PI()/128 )", 1.496605762665489);
CHECK_EVAL("TAN( 21*2*PI()/128 )", 1.668399205583507);
CHECK_EVAL("TAN( 22*2*PI()/128 )", 1.870868411789389);
CHECK_EVAL("TAN( 23*2*PI()/128 )", 2.114322357548640);
CHECK_EVAL("TAN( 24*2*PI()/128 )", 2.414213562373095);
CHECK_EVAL("TAN( 25*2*PI()/128 )", 2.794812772490477);
CHECK_EVAL("TAN( 26*2*PI()/128 )", 3.296558208938321);
CHECK_EVAL("TAN( 27*2*PI()/128 )", 3.992223783770083);
CHECK_EVAL("TAN( 28*2*PI()/128 )", 5.027339492125846);
CHECK_EVAL("TAN( 29*2*PI()/128 )", 6.741452405414988);
CHECK_EVAL("TAN( 30*2*PI()/128 )", 10.153170387608842);
CHECK_EVAL("TAN( 31*2*PI()/128 )", 20.355467624987138);
CHECK_EVAL("TAN( 33*2*PI()/128 )", -20.355467624987192);
CHECK_EVAL("TAN( 34*2*PI()/128 )", -10.153170387608855);
CHECK_EVAL("TAN( 35*2*PI()/128 )", -6.741452405414994);
CHECK_EVAL("TAN( 36*2*PI()/128 )", -5.027339492125850);
CHECK_EVAL("TAN( 37*2*PI()/128 )", -3.992223783770084);
CHECK_EVAL("TAN( 38*2*PI()/128 )", -3.296558208938323);
CHECK_EVAL("TAN( 39*2*PI()/128 )", -2.794812772490478);
CHECK_EVAL("TAN( 40*2*PI()/128 )", -2.4142135623731);
CHECK_EVAL("TAN( 41*2*PI()/128 )", -2.114322357548642);
CHECK_EVAL("TAN( 42*2*PI()/128 )", -1.870868411789389);
CHECK_EVAL("TAN( 43*2*PI()/128 )", -1.668399205583508);
CHECK_EVAL("TAN( 44*2*PI()/128 )", -1.496605762665490);
CHECK_EVAL("TAN( 45*2*PI()/128 )", -1.348343913486720);
CHECK_EVAL("TAN( 46*2*PI()/128 )", -1.218503525587977);
CHECK_EVAL("TAN( 47*2*PI()/128 )", -1.103329975733476);
CHECK_EVAL("TAN( 48*2*PI()/128 )", -1.000000000000000);
CHECK_EVAL("TAN( 49*2*PI()/128 )", -0.906347169019148);
CHECK_EVAL("TAN( 50*2*PI()/128 )", -0.820678790828660);
CHECK_EVAL("TAN( 51*2*PI()/128 )", -0.741650546272036);
CHECK_EVAL("TAN( 52*2*PI()/128 )", -0.668178637919299);
CHECK_EVAL("TAN( 53*2*PI()/128 )", -0.599376933681924);
CHECK_EVAL("TAN( 54*2*PI()/128 )", -0.534511135950792);
CHECK_EVAL("TAN( 55*2*PI()/128 )", -0.472964775891320);
CHECK_EVAL("TAN( 56*2*PI()/128 )", -0.414213562373095);
CHECK_EVAL("TAN( 57*2*PI()/128 )", -0.357805721314524);
CHECK_EVAL("TAN( 58*2*PI()/128 )", -0.303346683607342);
CHECK_EVAL("TAN( 59*2*PI()/128 )", -0.250486960191306);
CHECK_EVAL("TAN( 60*2*PI()/128 )", -0.198912367379658);
CHECK_EVAL("TAN( 61*2*PI()/128 )", -0.148335987538347);
CHECK_EVAL("TAN( 62*2*PI()/128 )", -0.0984914033571645);
CHECK_EVAL("TAN( 63*2*PI()/128 )", -0.0491268497694672);
CHECK_EVAL("TAN( 64*2*PI()/128 )", -0.000000000000000);
CHECK_EVAL("TAN( 65*2*PI()/128 )", 0.049126849769467);
CHECK_EVAL("TAN( 66*2*PI()/128 )", 0.0984914033571642);
CHECK_EVAL("TAN( 67*2*PI()/128 )", 0.148335987538347);
CHECK_EVAL("TAN( 68*2*PI()/128 )", 0.198912367379658);
CHECK_EVAL("TAN( 69*2*PI()/128 )", 0.250486960191305);
CHECK_EVAL("TAN( 70*2*PI()/128 )", 0.303346683607342);
CHECK_EVAL("TAN( 71*2*PI()/128 )", 0.357805721314524);
CHECK_EVAL("TAN( 72*2*PI()/128 )", 0.414213562373095);
CHECK_EVAL("TAN( 73*2*PI()/128 )", 0.472964775891320);
CHECK_EVAL("TAN( 74*2*PI()/128 )", 0.534511135950792);
CHECK_EVAL("TAN( 75*2*PI()/128 )", 0.599376933681923);
CHECK_EVAL("TAN( 76*2*PI()/128 )", 0.668178637919298);
CHECK_EVAL("TAN( 77*2*PI()/128 )", 0.741650546272035);
CHECK_EVAL("TAN( 78*2*PI()/128 )", 0.820678790828660);
CHECK_EVAL("TAN( 79*2*PI()/128 )", 0.906347169019147);
CHECK_EVAL("TAN( 80*2*PI()/128 )", 1.000000000000000);
CHECK_EVAL("TAN( 81*2*PI()/128 )", 1.103329975733475);
CHECK_EVAL("TAN( 82*2*PI()/128 )", 1.218503525587975);
CHECK_EVAL("TAN( 83*2*PI()/128 )", 1.348343913486721);
CHECK_EVAL("TAN( 84*2*PI()/128 )", 1.496605762665489);
CHECK_EVAL("TAN( 85*2*PI()/128 )", 1.668399205583507);
CHECK_EVAL("TAN( 86*2*PI()/128 )", 1.870868411789388);
CHECK_EVAL("TAN( 87*2*PI()/128 )", 2.114322357548638);
CHECK_EVAL("TAN( 88*2*PI()/128 )", 2.414213562373091);
CHECK_EVAL("TAN( 89*2*PI()/128 )", 2.794812772490478);
CHECK_EVAL("TAN( 90*2*PI()/128 )", 3.296558208938320);
CHECK_EVAL("TAN( 91*2*PI()/128 )", 3.992223783770080);
CHECK_EVAL("TAN( 92*2*PI()/128 )", 5.027339492125837);
CHECK_EVAL("TAN( 93*2*PI()/128 )", 6.741452405414962);
CHECK_EVAL("TAN( 94*2*PI()/128 )", 10.1531703876089);
CHECK_EVAL("TAN( 95*2*PI()/128 )", 20.3554676249872);
CHECK_EVAL("TAN( 97*2*PI()/128 )", -20.3554676249873);
CHECK_EVAL("TAN( 98*2*PI()/128 )", -10.1531703876089);
CHECK_EVAL("TAN( 99*2*PI()/128 )", -6.741452405414980);
CHECK_EVAL("TAN( 100*2*PI()/128 )", -5.027339492125847);
CHECK_EVAL("TAN( 101*2*PI()/128 )", -3.992223783770086);
CHECK_EVAL("TAN( 102*2*PI()/128 )", -3.296558208938324);
CHECK_EVAL("TAN( 103*2*PI()/128 )", -2.794812772490481);
CHECK_EVAL("TAN( 104*2*PI()/128 )", -2.414213562373094);
CHECK_EVAL("TAN( 105*2*PI()/128 )", -2.114322357548640);
CHECK_EVAL("TAN( 106*2*PI()/128 )", -1.870868411789390);
CHECK_EVAL("TAN( 107*2*PI()/128 )", -1.668399205583508);
CHECK_EVAL("TAN( 108*2*PI()/128 )", -1.496605762665490);
CHECK_EVAL("TAN( 109*2*PI()/128 )", -1.348343913486722);
CHECK_EVAL("TAN( 110*2*PI()/128 )", -1.218503525587976);
CHECK_EVAL("TAN( 111*2*PI()/128 )", -1.103329975733476);
CHECK_EVAL("TAN( 112*2*PI()/128 )", -1.000000000000000);
CHECK_EVAL("TAN( 113*2*PI()/128 )", -0.906347169019148);
CHECK_EVAL("TAN( 114*2*PI()/128 )", -0.820678790828661);
CHECK_EVAL("TAN( 115*2*PI()/128 )", -0.741650546272035);
CHECK_EVAL("TAN( 116*2*PI()/128 )", -0.668178637919299);
CHECK_EVAL("TAN( 117*2*PI()/128 )", -0.599376933681924);
CHECK_EVAL("TAN( 118*2*PI()/128 )", -0.534511135950792);
CHECK_EVAL("TAN( 119*2*PI()/128 )", -0.472964775891321);
CHECK_EVAL("TAN( 120*2*PI()/128 )", -0.414213562373096);
CHECK_EVAL("TAN( 121*2*PI()/128 )", -0.357805721314524);
CHECK_EVAL("TAN( 122*2*PI()/128 )", -0.303346683607343);
CHECK_EVAL("TAN( 123*2*PI()/128 )", -0.250486960191306);
CHECK_EVAL("TAN( 124*2*PI()/128 )", -0.198912367379658);
CHECK_EVAL("TAN( 125*2*PI()/128 )", -0.148335987538348);
CHECK_EVAL("TAN( 126*2*PI()/128 )", -0.0984914033571642);
CHECK_EVAL("TAN( 127*2*PI()/128 )", -0.0491268497694673);
}
void TestTrigFunctions::testCSC()
{
CHECK_EVAL("CSC(1)", 1.1883951057781212);
}
void TestTrigFunctions::testCSCH()
{
CHECK_EVAL("CSCH(1)", 0.8509181282393215);
}
void TestTrigFunctions::testSEC()
{
CHECK_EVAL("SEC(1)", 1.850815717680925617);
}
void TestTrigFunctions::testSECH()
{
CHECK_EVAL("SECH(1)", 0.6480542736638853996);
}
-QTEST_KDEMAIN(TestTrigFunctions, GUI)
+QTEST_MAIN(TestTrigFunctions)
diff --git a/sheets/tests/TestTrigFunctions.h b/sheets/tests/TestTrigFunctions.h
index cdfbca7ed40..baebc51f96e 100644
--- a/sheets/tests/TestTrigFunctions.h
+++ b/sheets/tests/TestTrigFunctions.h
@@ -1,57 +1,56 @@
/* This file is part of the KDE project
Copyright 2007 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_TRIG_FUNCTIONS
#define CALLIGRA_SHEETS_TEST_TRIG_FUNCTIONS
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
#include <Value.h>
namespace Calligra
{
namespace Sheets
{
class TestTrigFunctions: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testCOS();
void testCOSH();
void testPI();
void testSIN();
void testSINH();
void testTAN();
void testCSC();
void testCSCH();
void testSEC();
void testSECH();
private:
Value evaluate(const QString&);
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_TRIG_FUNCTIONS
diff --git a/sheets/tests/TestUtil.h b/sheets/tests/TestUtil.h
index 2d7b3f8d511..a028602b0cd 100644
--- a/sheets/tests/TestUtil.h
+++ b/sheets/tests/TestUtil.h
@@ -1,43 +1,43 @@
/* This file is part of the KDE project
Copyright 2011 Thorsten Zachmann <zachmann@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_UTIL
#define CALLIGRA_SHEETS_TEST_UTIL
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class TestUtil: public QObject
{
Q_OBJECT
private Q_SLOTS:
void testDecodeFormula_data();
void testDecodeFormula();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_UTIL
diff --git a/sheets/tests/TestValue.cpp b/sheets/tests/TestValue.cpp
index 5e958bcf320..effd77d7394 100644
--- a/sheets/tests/TestValue.cpp
+++ b/sheets/tests/TestValue.cpp
@@ -1,360 +1,360 @@
/* This file is part of the KDE project
Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
Copyright 2004 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestValue.h"
#include "TestKspreadCommon.h"
#include "CalculationSettings.h"
void TestValue::testEmpty()
{
Value* v1;
// empty value
v1 = new Value();
QCOMPARE(v1->type(), Value::Empty);
delete v1;
}
void TestValue::testBoolean()
{
Value* v1;
// boolean value (true)
v1 = new Value(true);
QCOMPARE(v1->type(), Value::Boolean);
QCOMPARE(v1->asBoolean(), true);
*v1 = Value(1); // dummy
*v1 = Value(true);
QCOMPARE(v1->type(), Value::Boolean);
QCOMPARE(v1->asBoolean(), true);
delete v1;
// boolean value (false)
v1 = new Value(false);
QCOMPARE(v1->type(), Value::Boolean);
QCOMPARE(v1->asBoolean(), false);
*v1 = Value(4); // dummy
*v1 = Value(false);
QCOMPARE(v1->type(), Value::Boolean);
QCOMPARE(v1->asBoolean(), false);
delete v1;
}
void TestValue::testInteger()
{
Value* v1;
// integer value
v1 = new Value(1977);
QCOMPARE(v1->type(), Value::Integer);
QCOMPARE(v1->asInteger(), (qint64)1977);
*v1 = Value(false); // dummy
*v1 = Value(14);
QCOMPARE(v1->type(), Value::Integer);
QCOMPARE(v1->isInteger(), true);
QCOMPARE(v1->isFloat(), false);
QCOMPARE(v1->isString(), false);
QCOMPARE(v1->isNumber(), true);
QCOMPARE(v1->asInteger(), (qint64)14);
delete v1;
}
void TestValue::testFloat()
{
Value* v1;
// floating-point value
v1 = new Value(M_PI);
QCOMPARE(v1->type(), Value::Float);
QCOMPARE(numToDouble(v1->asFloat()), (long double) M_PI);
*v1 = Value(false); // dummy
*v1 = Value(14.03l);
QCOMPARE(v1->type(), Value::Float);
QCOMPARE(v1->isInteger(), false);
QCOMPARE(v1->isFloat(), true);
QCOMPARE(v1->isString(), false);
QCOMPARE(v1->isNumber(), true);
QCOMPARE(numToDouble(v1->asFloat()), 14.03l);
delete v1;
}
void TestValue::testString()
{
Value* v1;
Value* v2;
// string value
v1 = new Value(QString("Ailinon"));
QCOMPARE(v1->type(), Value::String);
QCOMPARE(v1->asString(), QString("Ailinon"));
*v1 = Value(7); // dummy
*v1 = Value(QString("spreadsheet"));
QCOMPARE(v1->type(), Value::String);
QCOMPARE(v1->isInteger(), false);
QCOMPARE(v1->isFloat(), false);
QCOMPARE(v1->isString(), true);
QCOMPARE(v1->isNumber(), false);
QCOMPARE(v1->asString(), QString("spreadsheet"));
delete v1;
// equality
v1 = new Value(Value::String);
v2 = new Value(Value::String);
QCOMPARE(*v1, *v2);
*v1 = Value(QString("spreadsheet"));
*v2 = Value(QString("spreadsheet"));
QCOMPARE(*v1, *v2);
delete v1;
delete v2;
}
void TestValue::testDate()
{
Value* v1;
// check all (valid) dates from 1900 to 2050
// note: bail on first error immediately
CalculationSettings calculationSettings;
QDate refDate(1899, 12, 31);
v1 = new Value();
bool date_error = 0;
for (unsigned y = 1900; !date_error && y < 2050; ++y)
for (unsigned m = 1; !date_error && m <= 12; ++m)
for (unsigned d = 1; !date_error && d <= 31; ++d) {
QDate dv1 = QDate(y, m, d);
if (!dv1.isValid()) continue;
long double serialNo = -dv1.daysTo(refDate) + 1.0;
*v1 = Value(Value(dv1, &calculationSettings));
QCOMPARE(numToDouble(v1->asFloat()), serialNo);
date_error = v1->asFloat() != serialNo;
}
delete v1;
}
void TestValue::testTime()
{
CalculationSettings calculationSettings;
Value* v1;
// time value
v1 = new Value();
*v1 = Value(Value(QTime(0, 0, 0), &calculationSettings));
QCOMPARE(v1->type(), Value::Float);
for (unsigned h = 0; h < 24; ++h)
for (unsigned m = 0; m < 60; ++m)
for (unsigned s = 0; s < 60; ++s) {
QTime t1 = QTime(h, m, s);
*v1 = Value(Value(t1, &calculationSettings));
QTime t2 = v1->asTime(&calculationSettings);
QCOMPARE(t1.hour(), t2.hour());
QCOMPARE(t1.minute(), t2.minute());
QCOMPARE(t1.second(), t2.second());
QCOMPARE(t1.msec(), t2.msec());
}
delete v1;
// time value (msec)
v1 = new Value();
*v1 = Value(Value(QTime(0, 0, 0), &calculationSettings));
QCOMPARE(v1->type(), Value::Float);
for (unsigned ms = 0; ms < 1000; ++ms) {
QTime t1 = QTime(1, 14, 2, ms);
*v1 = Value(Value(t1, &calculationSettings));
QTime t2 = v1->asTime(&calculationSettings);
QCOMPARE(t1.hour(), t2.hour());
QCOMPARE(t1.minute(), t2.minute());
QCOMPARE(t1.second(), t2.second());
QCOMPARE(t1.msec(), t2.msec());
}
delete v1;
}
void TestValue::testError()
{
Value* v1;
Value* v2;
// TODO error values
// TODO compare values
// TODO add, sub, mul, div values
// TODO pow
// equality
v1 = new Value(Value::Error);
v2 = new Value(Value::Error);
QCOMPARE(*v1, *v2);
*v1 = Value(Value::errorVALUE());
*v2 = Value(Value::errorVALUE());
QCOMPARE(*v1, *v2);
delete v1;
delete v2;
}
void TestValue::testArray()
{
Value* v1;
Value* v2;
// array
v1 = new Value(Value::Array);
QCOMPARE(v1->type(), Value::Array);
QCOMPARE(v1->columns(), (unsigned)1);
QCOMPARE(v1->rows(), (unsigned)1);
delete v1;
// check empty value in array
v1 = new Value(Value::Array);
QCOMPARE(v1->type(), Value::Array);
v2 = new Value(v1->element(0, 0));
QCOMPARE(v2->type(), Value::Empty);
delete v1;
// fill simple 1x1 array
v1 = new Value(Value::Array);
QCOMPARE(v1->type(), Value::Array);
v2 = new Value(14.3l);
v1->setElement(0, 0, *v2);
delete v2;
v2 = new Value(v1->element(0, 0));
QCOMPARE(v2->type(), Value::Float);
QCOMPARE(numToDouble(v2->asFloat()), 14.3l);
delete v2;
delete v1;
// stress test, array of 1000x1000
v1 = new Value(Value::Array);
QCOMPARE(v1->type(), Value::Array);
for (unsigned r = 0; r < 1000; ++r)
for (unsigned c = 0; c < 1000; ++c) {
int index = 1000 * r + c;
v1->setElement(c, r, Value(index));
}
int array_error = 0;
for (unsigned c = 0; !array_error && c < 1000; ++c)
for (unsigned r = 0; !array_error && r < 1000; ++r) {
int index = 1000 * r + c;
v2 = new Value(v1->element(c, r));
if (v2->type() != Value::Integer) ++array_error;
if (v2->asInteger() != index) ++array_error;
delete v2;
}
QCOMPARE(array_error, (int)0);
delete v1;
// assignment of array value
v1 = new Value(Value::Array);
QCOMPARE(v1->type(), Value::Array);
v1->setElement(1, 1, Value(44.3l));
v1->setElement(0, 1, Value(34.3l));
v1->setElement(1, 0, Value(24.3l));
v1->setElement(0, 0, Value(14.3l));
v2 = new Value(*v1); // v2 is now also an array
delete v1;
v1 = new Value(v2->element(0, 0));
QCOMPARE(v1->type(), Value::Float);
QCOMPARE(numToDouble(v1->asFloat()), 14.3l);
delete v1;
delete v2;
// equality
v1 = new Value(Value::Array);
v2 = new Value(Value::Array);
QCOMPARE(*v1, *v2);
v1->setElement(0, 0, Value(1));
v1->setElement(0, 1, Value(2));
v2->setElement(0, 0, Value(1));
v2->setElement(0, 1, Value(2));
QCOMPARE(*v1, *v2);
delete v1;
delete v2;
}
void TestValue::testCopy()
{
Value* v1;
Value* v2;
// copy value
v1 = new Value();
*v1 = Value(14.3l);
v2 = new Value(*v1);
QCOMPARE(v1->type(), Value::Float);
QCOMPARE(v2->type(), Value::Float);
QCOMPARE(numToDouble(v1->asFloat()), 14.3l);
QCOMPARE(numToDouble(v2->asFloat()), 14.3l);
delete v1;
delete v2;
}
void TestValue::testAssignment()
{
Value* v1;
Value* v2;
// value assignment
v1 = new Value(14.3l);
v2 = new Value(true);
*v2 = *v1;
QCOMPARE(v1->type(), Value::Float);
QCOMPARE(v2->type(), Value::Float);
QCOMPARE(numToDouble(v1->asFloat()), 14.3l);
QCOMPARE(numToDouble(v2->asFloat()), 14.3l);
delete v1;
delete v2;
// test copying/detaching of string values (QString*)
v1 = new Value("Hello");
v2 = new Value(true);
*v2 = *v1;
QCOMPARE(v1->type(), Value::String);
QCOMPARE(v2->type(), Value::String);
QCOMPARE(v1->asString(), QString("Hello"));
QCOMPARE(v2->asString(), QString("Hello"));
*v2 = Value(QString("World"));
QCOMPARE(v1->asString(), QString("Hello"));
QCOMPARE(v2->asString(), QString("World"));
delete v1;
delete v2;
// test copying/detaching of arrays (ValueArray*)
v1 = new Value(Value::Array);
v1->setElement(0, 0, Value(1));
v1->setElement(0, 1, Value(2));
v2 = new Value(true);
*v2 = *v1;
QCOMPARE(v1->type(), Value::Array);
QCOMPARE(v2->type(), Value::Array);
QCOMPARE(v1->element(0, 0), Value(1));
QCOMPARE(v1->element(0, 1), Value(2));
QCOMPARE(v2->element(0, 0), Value(1));
QCOMPARE(v2->element(0, 1), Value(2));
v2->setElement(0, 0, Value(3));
QCOMPARE(v1->element(0, 0), Value(1));
QCOMPARE(v1->element(0, 1), Value(2));
QCOMPARE(v2->element(0, 0), Value(3));
QCOMPARE(v2->element(0, 1), Value(2));
delete v1;
delete v2;
}
-QTEST_KDEMAIN(TestValue, GUI)
+QTEST_MAIN(TestValue)
diff --git a/sheets/tests/TestValue.h b/sheets/tests/TestValue.h
index aa2cb962aef..477d98441ef 100644
--- a/sheets/tests/TestValue.h
+++ b/sheets/tests/TestValue.h
@@ -1,51 +1,50 @@
/* This file is part of the KDE project
Copyright 2004 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_VALUE
#define CALLIGRA_SHEETS_TEST_VALUE
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class TestValue: public QObject
{
Q_OBJECT
private Q_SLOTS:
void testEmpty();
void testBoolean();
void testInteger();
void testFloat();
void testString();
void testDate();
void testTime();
void testError();
void testArray();
void testCopy();
void testAssignment();
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_VALUE
diff --git a/sheets/tests/TestValueFormatter.cpp b/sheets/tests/TestValueFormatter.cpp
index f78c8780adb..c82f93f48d5 100644
--- a/sheets/tests/TestValueFormatter.cpp
+++ b/sheets/tests/TestValueFormatter.cpp
@@ -1,237 +1,237 @@
/* This file is part of the KDE project
Copyright 2009 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "TestValueFormatter.h"
#include <ValueFormatter.h>
#include <CalculationSettings.h>
#include <ValueConverter.h>
#include <ValueParser.h>
-#include <qtest_kde.h>
+#include <QTest>
Q_DECLARE_METATYPE(Calligra::Sheets::Format::Type)
Q_DECLARE_METATYPE(Calligra::Sheets::Style::FloatFormat)
using namespace Calligra::Sheets;
class PublicValueFormatter : public ValueFormatter
{
public:
explicit PublicValueFormatter(const ValueConverter* converter)
: ValueFormatter(converter) {}
using ValueFormatter::fractionFormat;
using ValueFormatter::createNumberFormat;
};
void TestValueFormatter::initTestCase()
{
qRegisterMetaType<Format::Type>();
m_calcsettings = new CalculationSettings();
m_parser = new ValueParser(m_calcsettings);
m_converter = new ValueConverter(m_parser);
}
void TestValueFormatter::cleanupTestCase()
{
delete m_converter;
delete m_parser;
delete m_calcsettings;
}
void TestValueFormatter::testFractionFormat_data()
{
QTest::addColumn<double>("value");
QTest::addColumn<Format::Type>("formatType");
QTest::addColumn<QString>("result");
// fraction_half
QTest::newRow("half 0.0") << 0.0 << Format::fraction_half << "0";
QTest::newRow("half 1.0") << 1.0 << Format::fraction_half << "1";
QTest::newRow("half 1.5") << 1.5 << Format::fraction_half << "1 1/2";
QTest::newRow("half 1.4") << 1.4 << Format::fraction_half << "1 1/2";
QTest::newRow("half 1.6") << 1.6 << Format::fraction_half << "1 1/2";
QTest::newRow("half 0.9") << 0.9 << Format::fraction_half << "1";
QTest::newRow("half 1.1") << 1.1 << Format::fraction_half << "1";
QTest::newRow("half -0.2") << -0.2 << Format::fraction_half << "-0";
QTest::newRow("half -0.4") << -0.4 << Format::fraction_half << "-1/2";
QTest::newRow("half -0.6") << -0.6 << Format::fraction_half << "-1/2";
QTest::newRow("half -0.9") << -0.9 << Format::fraction_half << "-1";
// fraction_quarter
QTest::newRow("quarter 0.0") << 0.0 << Format::fraction_quarter << "0";
QTest::newRow("quarter 1.0") << 1.0 << Format::fraction_quarter << "1";
QTest::newRow("quarter 0.1") << 0.1 << Format::fraction_quarter << "0";
QTest::newRow("quarter 0.2") << 0.2 << Format::fraction_quarter << "1/4";
QTest::newRow("quarter 0.3") << 0.3 << Format::fraction_quarter << "1/4";
QTest::newRow("quarter 0.5") << 0.5 << Format::fraction_quarter << "2/4";
QTest::newRow("quarter 0.8") << 0.8 << Format::fraction_quarter << "3/4";
QTest::newRow("quarter 0.9") << 0.9 << Format::fraction_quarter << "1";
QTest::newRow("quarter 1.1") << 1.1 << Format::fraction_quarter << "1";
QTest::newRow("quarter 1.2") << 1.2 << Format::fraction_quarter << "1 1/4";
QTest::newRow("quarter -0.1") << -0.1 << Format::fraction_quarter << "-0";
QTest::newRow("quarter -0.2") << -0.2 << Format::fraction_quarter << "-1/4";
QTest::newRow("quarter -0.3") << -0.3 << Format::fraction_quarter << "-1/4";
QTest::newRow("quarter -0.5") << -0.5 << Format::fraction_quarter << "-2/4";
QTest::newRow("quarter -0.8") << -0.8 << Format::fraction_quarter << "-3/4";
QTest::newRow("quarter -0.9") << -0.9 << Format::fraction_quarter << "-1";
// fraction_eighth
QTest::newRow("eighth 0.0") << 0.0 << Format::fraction_eighth << "0";
QTest::newRow("eighth 1.0") << 1.0 << Format::fraction_eighth << "1";
QTest::newRow("eighth 0.05") << 0.05 << Format::fraction_eighth << "0";
QTest::newRow("eighth 0.10") << 0.10 << Format::fraction_eighth << "1/8";
QTest::newRow("eighth 0.15") << 0.15 << Format::fraction_eighth << "1/8";
QTest::newRow("eighth 0.25") << 0.25 << Format::fraction_eighth << "2/8";
QTest::newRow("eighth 0.50") << 0.50 << Format::fraction_eighth << "4/8";
QTest::newRow("eighth 0.90") << 0.90 << Format::fraction_eighth << "7/8";
QTest::newRow("eighth 0.95") << 0.95 << Format::fraction_eighth << "1";
QTest::newRow("eighth 1.05") << 1.05 << Format::fraction_eighth << "1";
QTest::newRow("eighth 1.10") << 1.10 << Format::fraction_eighth << "1 1/8";
QTest::newRow("eighth -0.05") << -0.05 << Format::fraction_eighth << "-0";
QTest::newRow("eighth -0.10") << -0.10 << Format::fraction_eighth << "-1/8";
QTest::newRow("eighth -0.15") << -0.15 << Format::fraction_eighth << "-1/8";
QTest::newRow("eighth -0.10") << -0.25 << Format::fraction_eighth << "-2/8";
QTest::newRow("eighth -0.90") << -0.90 << Format::fraction_eighth << "-7/8";
QTest::newRow("eighth -0.95") << -0.95 << Format::fraction_eighth << "-1";
// fraction_sixteenth
// fraction_tenth
QTest::newRow("tenth 0.0") << 0.0 << Format::fraction_tenth << "0";
QTest::newRow("tenth 1.0") << 1.0 << Format::fraction_tenth << "1";
QTest::newRow("tenth 0.04") << 0.04 << Format::fraction_tenth << "0";
QTest::newRow("tenth 0.06") << 0.06 << Format::fraction_tenth << "1/10";
QTest::newRow("tenth 0.14") << 0.14 << Format::fraction_tenth << "1/10";
QTest::newRow("tenth 0.53") << 0.53 << Format::fraction_tenth << "5/10";
QTest::newRow("tenth 0.94") << 0.94 << Format::fraction_tenth << "9/10";
QTest::newRow("tenth 0.97") << 0.97 << Format::fraction_tenth << "1";
QTest::newRow("tenth 1.02") << 1.02 << Format::fraction_tenth << "1";
QTest::newRow("tenth 1.47") << 1.47 << Format::fraction_tenth << "1 5/10";
QTest::newRow("tenth -0.04") << -0.04 << Format::fraction_tenth << "-0";
QTest::newRow("tenth -0.06") << -0.06 << Format::fraction_tenth << "-1/10";
QTest::newRow("tenth -0.14") << -0.14 << Format::fraction_tenth << "-1/10";
QTest::newRow("tenth -0.53") << -0.53 << Format::fraction_tenth << "-5/10";
QTest::newRow("tenth -0.94") << -0.94 << Format::fraction_tenth << "-9/10";
QTest::newRow("tenth -0.97") << -0.97 << Format::fraction_tenth << "-1";
// fraction_hundredth
// fraction_one_digit
QTest::newRow("one_digit 0.0") << 0.0 << Format::fraction_one_digit << "0";
QTest::newRow("one_digit 0.05") << 0.05 << Format::fraction_one_digit << "0";
QTest::newRow("one_digit 0.1") << 0.1 << Format::fraction_one_digit << "1/9";
QTest::newRow("one_digit 0.2") << 0.2 << Format::fraction_one_digit << "1/5";
QTest::newRow("one_digit 0.3") << 0.3 << Format::fraction_one_digit << "2/7";
QTest::newRow("one_digit 0.4") << 0.4 << Format::fraction_one_digit << "2/5";
QTest::newRow("one_digit 0.5") << 0.5 << Format::fraction_one_digit << "1/2";
QTest::newRow("one_digit 0.6") << 0.6 << Format::fraction_one_digit << "3/5";
QTest::newRow("one_digit 0.7") << 0.7 << Format::fraction_one_digit << "5/7";
QTest::newRow("one_digit 0.8") << 0.8 << Format::fraction_one_digit << "4/5";
QTest::newRow("one_digit 0.9") << 0.9 << Format::fraction_one_digit << "8/9";
QTest::newRow("one_digit 0.95") << 0.95 << Format::fraction_one_digit << "1";
QTest::newRow("one_digit 1.0") << 1.0 << Format::fraction_one_digit << "1";
QTest::newRow("one_digit 1.1") << 1.1 << Format::fraction_one_digit << "1 1/9";
QTest::newRow("one_digit 1.2") << 1.2 << Format::fraction_one_digit << "1 1/5";
QTest::newRow("one_digit 1.3") << 1.3 << Format::fraction_one_digit << "1 2/7";
QTest::newRow("one_digit -0.05") << -0.05 << Format::fraction_one_digit << "-0";
QTest::newRow("one_digit -0.1") << -0.1 << Format::fraction_one_digit << "-1/9";
QTest::newRow("one_digit -0.2") << -0.2 << Format::fraction_one_digit << "-1/5";
QTest::newRow("one_digit -0.3") << -0.3 << Format::fraction_one_digit << "-2/7";
// fraction_two_digits
QTest::newRow("two_digits 0.00") << 0.00 << Format::fraction_two_digits << "0";
QTest::newRow("two_digits 0.005") << 0.005 << Format::fraction_two_digits << "0";
QTest::newRow("two_digits 0.01") << 0.01 << Format::fraction_two_digits << "1/99";
QTest::newRow("two_digits 0.02") << 0.02 << Format::fraction_two_digits << "1/50";
QTest::newRow("two_digits 0.03") << 0.03 << Format::fraction_two_digits << "2/67";
QTest::newRow("two_digits 0.07") << 0.07 << Format::fraction_two_digits << "4/57";
QTest::newRow("two_digits 0.09") << 0.09 << Format::fraction_two_digits << "8/89";
QTest::newRow("two_digits 0.11") << 0.11 << Format::fraction_two_digits << "10/91";
QTest::newRow("two_digits 0.995") << 0.995 << Format::fraction_two_digits << "1";
QTest::newRow("two_digits 1.00") << 1.00 << Format::fraction_two_digits << "1";
QTest::newRow("two_digits 1.01") << 1.01 << Format::fraction_two_digits << "1 1/99";
QTest::newRow("two_digits 1.02") << 1.02 << Format::fraction_two_digits << "1 1/50";
QTest::newRow("two_digits 1.03") << 1.03 << Format::fraction_two_digits << "1 2/67";
QTest::newRow("two_digits -0.005") << -0.005 << Format::fraction_two_digits << "-0";
QTest::newRow("two_digits -0.01") << -0.01 << Format::fraction_two_digits << "-1/99";
QTest::newRow("two_digits -0.02") << -0.02 << Format::fraction_two_digits << "-1/50";
QTest::newRow("two_digits -0.03") << -0.03 << Format::fraction_two_digits << "-2/67";
QTest::newRow("two_digits -0.07") << -0.07 << Format::fraction_two_digits << "-4/57";
// fraction_three_digits
}
void TestValueFormatter::testFractionFormat()
{
QFETCH(double, value);
QFETCH(Format::Type, formatType);
QFETCH(QString, result);
Number num(value);
PublicValueFormatter fmt(m_converter);
QCOMPARE(fmt.fractionFormat(num, formatType), result);
}
void TestValueFormatter::testCreateNumberFormat_data()
{
QTest::addColumn<double>("value");
QTest::addColumn<int>("precision");
QTest::addColumn<Format::Type>("formatType");
QTest::addColumn<Style::FloatFormat>("floatFormat");
QTest::addColumn<QString>("currencySymbol");
QTest::addColumn<QString>("formatString");
QTest::addColumn<bool>("thousandsSep");
QTest::addColumn<QString>("result");
QTest::newRow("negative sign in format string") <<
-5.0 << 0 << Format::Number << Style::DefaultFloatFormat << "" << "(-.)" << false << "(-5)";
QTest::newRow("unspecified precision 1") <<
1.0 << -1 << Format::Number << Style::DefaultFloatFormat << "" << "0" << false << "1";
QTest::newRow("unspecified precision 0.5") <<
0.5 << -1 << Format::Number << Style::DefaultFloatFormat << "" << "0" << false << "0.5";
QTest::newRow("no thousands separators") <<
3000.0 << 0 << Format::Number << Style::DefaultFloatFormat << "" << "" << false << "3000";
QTest::newRow("with thousands separators") <<
3000.0 << 0 << Format::Number << Style::DefaultFloatFormat << "" << "" << true << "3,000";
}
void TestValueFormatter::testCreateNumberFormat()
{
QFETCH(double, value);
QFETCH(int, precision);
QFETCH(Format::Type, formatType);
QFETCH(Style::FloatFormat, floatFormat);
QFETCH(QString, currencySymbol);
QFETCH(QString, formatString);
QFETCH(bool, thousandsSep);
QFETCH(QString, result);
Number num(value);
PublicValueFormatter fmt(m_converter);
QCOMPARE(fmt.createNumberFormat(num, precision, formatType, floatFormat, currencySymbol, formatString, thousandsSep), result);
}
-QTEST_KDEMAIN(TestValueFormatter, GUI)
+QTEST_MAIN(TestValueFormatter)
diff --git a/sheets/tests/TestValueFormatter.h b/sheets/tests/TestValueFormatter.h
index 890b8d65d9e..f504c1d4845 100644
--- a/sheets/tests/TestValueFormatter.h
+++ b/sheets/tests/TestValueFormatter.h
@@ -1,55 +1,54 @@
/* This file is part of the KDE project
Copyright 2009 Marijn Kruisselbrink <mkruisselbrink@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; only
version 2 of the License.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef CALLIGRA_SHEETS_TEST_VALUEFORMATTER
#define CALLIGRA_SHEETS_TEST_VALUEFORMATTER
-#include <QtGui>
-#include <QtTest>
+#include <QObject>
namespace Calligra
{
namespace Sheets
{
class CalculationSettings;
class ValueParser;
class ValueConverter;
class TestValueFormatter: public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void testFractionFormat_data();
void testFractionFormat();
void testCreateNumberFormat_data();
void testCreateNumberFormat();
private:
CalculationSettings* m_calcsettings;
ValueParser* m_parser;
ValueConverter* m_converter;
};
} // namespace Sheets
} // namespace Calligra
#endif // CALLIGRA_SHEETS_TEST_VALUEFORMATTER
diff --git a/stage/part/CMakeLists.txt b/stage/part/CMakeLists.txt
index c05ca9b06c2..27dccf97f2c 100644
--- a/stage/part/CMakeLists.txt
+++ b/stage/part/CMakeLists.txt
@@ -1,203 +1,203 @@
project(stagepart)
include_directories( ${KOMAIN_INCLUDES} ${KOPAGEAPP_INCLUDES} ${Boost_INCLUDE_DIR})
-if (!WIN32) ## Disable tests in Windows whilst they break builds
- add_subdirectory(tests)
+if (NOT WIN32) ## Disable tests in Windows whilst they break builds
+ add_subdirectory(tests)
endif()
add_subdirectory(tools)
### calligrastageprivate ###
set( calligrastageprivate_LIB_SRCS
KPrFactory.cpp
KPrDocument.cpp
KPrDeclarations.cpp
KPrPart.cpp
KPrView.cpp
KPrViewModePresentation.cpp
KPrViewModeNotes.cpp
KPrViewModeSlidesSorter.cpp
KPrViewModePreviewPageEffect.cpp
KPrViewModePreviewShapeAnimations.cpp
KPrPresentationTool.cpp
KPrAnimationDirector.cpp
KPrShapeAnimations.cpp
KPrShapeManagerAnimationStrategy.cpp
KPrShapeManagerDisplayMasterStrategy.cpp
KPrPageData.cpp
KPrPage.cpp
KPrMasterPage.cpp
KPrNotes.cpp
KPrSoundData.cpp
KPrSoundCollection.cpp
KPrEventActionData.cpp
KPrEventActionWidget.cpp
KPrPageApplicationData.cpp
KPrShapeApplicationData.cpp
KPrCustomSlideShows.cpp
KPrPresenterViewBaseInterface.cpp
KPrPresenterViewInterface.cpp
KPrPresenterViewSlidesInterface.cpp
KPrPresenterViewToolWidget.cpp
KPrPresenterViewWidget.cpp
KPrEndOfSlideShowPage.cpp
KPrPlaceholderShape.cpp
KPrPlaceholderShapeFactory.cpp
KPrPlaceholderStrategy.cpp
KPrPlaceholderPictureStrategy.cpp
KPrPlaceholderTextStrategy.cpp
KPrPresentationHighlightWidget.cpp
KPrPresentationDrawWidget.cpp
KPrPresentationBlackWidget.cpp
KPrPresentationStrategy.cpp
KPrPresentationHighlightStrategy.cpp
KPrPresentationBlackStrategy.cpp
KPrPresentationStrategyBase.cpp
KPrPresentationToolEventForwarder.cpp
KPrPresentationDrawStrategy.cpp
KPrPageSelectStrategyBase.cpp
KPrPageSelectStrategyFixed.cpp
KPrPageSelectStrategyActive.cpp
KPrDurationParser.cpp
KPrHtmlExport.cpp
KPrHtmlExportUiDelegate.cpp
KPrPicturesImport.cpp
KPrPdfPrintJob.cpp
KPrSlidesSorterDocumentModel.cpp
KPrSlidesManagerView.cpp
KPrCustomSlideShowsModel.cpp
KPrSlidesSorterItemDelegate.cpp
KPrPageLayoutWidget.cpp
commands/KPrAnimationCreateCommand.cpp
commands/KPrAnimationRemoveCommand.cpp
commands/KPrPageEffectSetCommand.cpp
commands/KPrPageLayoutCommand.cpp
commands/KPrEditCustomSlideShowsCommand.cpp
commands/KPrAddCustomSlideShowCommand.cpp
commands/KPrDelCustomSlideShowCommand.cpp
commands/KPrRenameCustomSlideShowCommand.cpp
commands/KPrDeleteSlidesCommand.cpp
commands/KPrEditAnimationTimeLineCommand.cpp
commands/KPrAnimationEditNodeTypeCommand.cpp
commands/KPrReorderAnimationCommand.cpp
commands/KPrReplaceAnimationCommand.cpp
dockers/KPrPreviewWidget.cpp
pageeffects/KPrPageEffectRunner.cpp
pageeffects/KPrPageEffect.cpp
pageeffects/KPrPageEffectStrategy.cpp
pageeffects/KPrPageEffectFactory.cpp
pageeffects/KPrPageEffectRegistry.cpp
animations/KPrAnimationBase.cpp
animations/KPrAnimSet.cpp
animations/KPrAnimate.cpp
animations/KPrAnimateColor.cpp
animations/KPrAnimateMotion.cpp
animations/KPrAnimateTransform.cpp
animations/KPrAnimTransitionFilter.cpp
animations/KPrAnimationFactory.cpp
animations/KPrAnimationCache.cpp
animations/KPrTextBlockPaintStrategy.cpp
animations/KPrShapeAnimation.cpp
animations/KPrAnimationStep.cpp
animations/KPrAnimationSubStep.cpp
animations/KPrAnimationLoader.cpp
animations/KPrAnimationData.cpp
animations/strategy/KPrAnimationValue.cpp
animations/strategy/KPrFormulaParser.cpp
animations/strategy/KPrAnimationAttribute.cpp
animations/strategy/KPrSmilValues.cpp
animations/strategy/KPrAttributeX.cpp
animations/strategy/KPrAttributeY.cpp
animations/strategy/KPrAttributeWidth.cpp
animations/strategy/KPrAttributeHeight.cpp
animations/strategy/KPrAttributeRotate.cpp
pagelayout/KPrPlaceholder.cpp
pagelayout/KPrPageLayout.cpp
pagelayout/KPrPageLayouts.cpp
pagelayout/KPrPageLayoutSharedSavingData.cpp
pagelayout/KPrPlaceholders.cpp
ui/KPrConfigureSlideShowDialog.cpp
ui/KPrConfigurePresenterViewDialog.cpp
ui/KPrPresentationToolWidget.cpp
ui/KPrHtmlExportDialog.cpp
tools/KPrPlaceholderTool.cpp
tools/KPrPlaceholderToolFactory.cpp
)
if(QT_QTDBUS_FOUND)
set( calligrastageprivate_LIB_SRCS
${calligrastageprivate_LIB_SRCS}
KPrViewAdaptor.cpp
KPrPresentationToolAdaptor.cpp
)
endif()
ki18n_wrap_ui(calligrastageprivate_LIB_SRCS
ui/KPrConfigureSlideShow.ui
ui/KPrConfigurePresenterView.ui
ui/KPrPresentationTool.ui
ui/KPrHtmlExport.ui
)
add_library(calligrastageprivate SHARED ${calligrastageprivate_LIB_SRCS})
target_link_libraries(calligrastageprivate kopageapp kowidgets kotextlayout KF5::Archive Qt5::Svg Qt5::OpenGL Qt5::WebKitWidgets)
target_link_libraries(calligrastageprivate LINK_INTERFACE_LIBRARIES kopageapp)
set_target_properties(calligrastageprivate PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} )
install(TARGETS calligrastageprivate ${INSTALL_TARGETS_DEFAULT_ARGS})
### calligrastagepart ###
set(calligrastagepart_PART_SRCS KPrFactoryInit.cpp )
add_library(calligrastagepart MODULE ${calligrastagepart_PART_SRCS})
kcoreaddons_desktop_to_json(calligrastagepart stagepart.desktop)
target_link_libraries(calligrastagepart calligrastageprivate )
install(TARGETS calligrastagepart DESTINATION ${CALLIGRA_PLUGIN_INSTALL_DIR})
### GUI files ###
install( FILES stage.rc stage_readonly.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/stage)
install( FILES stagerc DESTINATION ${CONFIG_INSTALL_DIR} )
### Predefined Animations ###
install(FILES
animations/animations.xml
DESTINATION ${DATA_INSTALL_DIR}/stage/animations)
### Include files ###
install( FILES
stage_export.h
KPrAnimationDirector.h
KPrCustomSlideShows.h
KPrDocument.h
KPrPage.h
KPrPageData.h
KPrDeclarations.h
KPrPresentationTool.h
KPrNotes.h
KPrShapeAnimations.h
KPrView.h
KPrViewModePresentation.h
DESTINATION ${INCLUDE_INSTALL_DIR}/stage/part)
install( FILES
animations/KPrAnimationData.h
animations/KPrAnimationStep.h
animations/KPrShapeAnimation.h
DESTINATION ${INCLUDE_INSTALL_DIR}/stage/part/animations)
install( FILES
pagelayout/KPrPlaceholders.h
DESTINATION ${INCLUDE_INSTALL_DIR}/stage/part/pagelayout)
diff --git a/stage/part/tests/TestAddCustomSlideShowCommand.cpp b/stage/part/tests/TestAddCustomSlideShowCommand.cpp
index f2a5d3affc5..5090c9f7c79 100644
--- a/stage/part/tests/TestAddCustomSlideShowCommand.cpp
+++ b/stage/part/tests/TestAddCustomSlideShowCommand.cpp
@@ -1,60 +1,61 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestAddCustomSlideShowCommand.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include "commands/KPrAddCustomSlideShowCommand.h"
#include "KPrCustomSlideShows.h"
#include "KPrCustomSlideShowsModel.h"
-#include <qtest_kde.h>
+
+#include <QTest>
void TestAddCustomSlideShowCommand::addCustomSlideShow()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
QString customShowName = "test 1";
KPrCustomSlideShowsModel model(&doc, 0);
KPrAddCustomSlideShowCommand cmd(&doc, &model, customShowName);
cmd.redo();
QCOMPARE(doc.customSlideShows()->names().count(), 1);
cmd.undo();
QCOMPARE(doc.customSlideShows()->names().count(), 0);
}
-QTEST_KDEMAIN(TestAddCustomSlideShowCommand, GUI)
+QTEST_MAIN(TestAddCustomSlideShowCommand)
diff --git a/stage/part/tests/TestAddCustomSlideShowCommand.h b/stage/part/tests/TestAddCustomSlideShowCommand.h
index 8b7293386aa..215a967fbb0 100644
--- a/stage/part/tests/TestAddCustomSlideShowCommand.h
+++ b/stage/part/tests/TestAddCustomSlideShowCommand.h
@@ -1,33 +1,33 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTADDCUSTOMSLIDESHOWCOMMAND_H
#define TESTADDCUSTOMSLIDESHOWCOMMAND_H
-#include <QtTest>
+#include <QObject>
class TestAddCustomSlideShowCommand : public QObject
{
Q_OBJECT
private Q_SLOTS:
void addCustomSlideShow();
};
#endif // TESTADDCUSTOMSLIDESHOWCOMMAND_H
diff --git a/stage/part/tests/TestCustomSlideShows.cpp b/stage/part/tests/TestCustomSlideShows.cpp
index c6d234237cf..5b872d4fe76 100644
--- a/stage/part/tests/TestCustomSlideShows.cpp
+++ b/stage/part/tests/TestCustomSlideShows.cpp
@@ -1,397 +1,397 @@
/* This file is part of the KDE project
* Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or ( at your option ) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestCustomSlideShows.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include <KPrCustomSlideShows.h>
-#include <qtest_kde.h>
+#include <QTest>
void TestCustomSlideShows::populateDoc(MockDocument &doc, QList<KoPAPageBase*> &slideList1, QList<KoPAPageBase*> &slideList2,
QString &customShowName1, QString &customShowName2)
{
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
//Create List 1 2 4 1 5
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page4);
slideList1.append(page1);
slideList1.append(page5);
//Create List 1 2 3 4 5
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
slideList2.append(page4);
slideList2.append(page5);
customShowName1 = "test1";
customShowName2 = "test2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
}
void TestCustomSlideShows::insertCustomSlideShow()
{
// create some slide shows and insert then then test if you can access them again
MockDocument doc;
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
QString customShowName1;
QString customShowName2;
populateDoc(doc, slideList1, slideList2, customShowName1, customShowName2);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
void TestCustomSlideShows::removeCustomSlideShow()
{
// create some slide shows and insert then then test if you can access them again
// then remove a´the slideshows again and test of they are no longer there
MockDocument doc;
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
QString customShowName1;
QString customShowName2;
populateDoc(doc, slideList1, slideList2, customShowName1, customShowName2);
doc.customSlideShows()->remove(customShowName1);
QList<QString> resultList;
resultList.append(customShowName2);
QCOMPARE(doc.customSlideShows()->names(), resultList);
}
void TestCustomSlideShows::updateCustomSlideShow()
{
// create some slide shows and insert then then test if you can access them again
// modify a slide show and update it
// test if you get the updated slide show
MockDocument doc;
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
QString customShowName1;
QString customShowName2;
populateDoc(doc, slideList1, slideList2, customShowName1, customShowName2);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
doc.customSlideShows()->update(customShowName1, slideList2);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList2);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
void TestCustomSlideShows::customSlideShowsNames()
{
// insert different slide shows
// test if you get the correct name of the slide shows
MockDocument doc;
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
QString customShowName1;
QString customShowName2;
populateDoc(doc, slideList1, slideList2, customShowName1, customShowName2);
QList<QString> resultList;
resultList.append(customShowName1);
resultList.append(customShowName2);
QCOMPARE(doc.customSlideShows()->names(), resultList);
}
void TestCustomSlideShows::getCustomSlideShowByName()
{
// insert some slide shows
// test if you can get each slide show correctly
}
void TestCustomSlideShows::addSlideToAllCustomSlideShows()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
//Create List 1 2 4 1
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page4);
slideList1.append(page1);
//Create List 1 2 3 4
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
slideList2.append(page4);
QString customShowName1 = "test 1";
QString customShowName2 = "test 2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
doc.customSlideShows()->addSlideToAll(page5, 0);
slideList1.insert(0, page5);
slideList2.insert(0, page5);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
void TestCustomSlideShows::addSlidesToAllCustomSlideShows()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
//Create List 1 2 1
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page1);
//Create List 1 2 3
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
QString customShowName1 = "test 1";
QString customShowName2 = "test 2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
QList<KoPAPageBase*> slideAddList;
slideAddList.append(page4);
slideAddList.append(page5);
doc.customSlideShows()->addSlidesToAll(slideAddList, 0);
slideList1.insert(0, page5);
slideList1.insert(0, page4);
slideList2.insert(0, page5);
slideList2.insert(0, page4);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
void TestCustomSlideShows::removeSlideFromAllCustomSlideShows()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
//Create List 1 2 4 1 5
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page4);
slideList1.append(page1);
slideList1.append(page5);
//Create List 1 2 3 4 5
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
slideList2.append(page4);
slideList2.append(page5);
QString customShowName1 = "test 1";
QString customShowName2 = "test 2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
doc.customSlideShows()->removeSlideFromAll(page1);
slideList1.removeAll(page1);
slideList2.removeAll(page1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
void TestCustomSlideShows::removeSlidesFromAllCustomSlideShows()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
QList<KoPAPageBase*> slideList1;
QList<KoPAPageBase*> slideList2;
//Create List 1 2 4 1 5
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page4);
slideList1.append(page1);
slideList1.append(page5);
//Create List 1 2 3 4 5
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
slideList2.append(page4);
slideList2.append(page5);
QString customShowName1 = "test 1";
QString customShowName2 = "test 2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
QList<KoPAPageBase*> slideRemoveList;
slideRemoveList.append(page4);
slideRemoveList.append(page5);
doc.customSlideShows()->removeSlidesFromAll(slideRemoveList);
slideList1.removeAll(page4);
slideList1.removeAll(page5);
slideList2.removeAll(page4);
slideList2.removeAll(page5);
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
-QTEST_KDEMAIN(TestCustomSlideShows, GUI)
+QTEST_MAIN(TestCustomSlideShows)
diff --git a/stage/part/tests/TestCustomSlideShows.h b/stage/part/tests/TestCustomSlideShows.h
index 719674456bd..93e46d876dd 100644
--- a/stage/part/tests/TestCustomSlideShows.h
+++ b/stage/part/tests/TestCustomSlideShows.h
@@ -1,45 +1,45 @@
/* This file is part of the KDE project
* Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or ( at your option ) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTCUSTOMSLIDESHOWS_H
#define TESTCUSTOMSLIDESHOWS_H
-#include <QtTest>
+#include <QObject>
class MockDocument;
class KoPAPageBase;
class TestCustomSlideShows : public QObject
{
Q_OBJECT
private:
void populateDoc(MockDocument &doc, QList<KoPAPageBase*> &slideList1, QList<KoPAPageBase*> &slideList2,
QString &customShowName1, QString &customShowName2);
private Q_SLOTS:
void insertCustomSlideShow();
void removeCustomSlideShow();
void updateCustomSlideShow();
void customSlideShowsNames();
void getCustomSlideShowByName();
void addSlideToAllCustomSlideShows();
void addSlidesToAllCustomSlideShows();
void removeSlideFromAllCustomSlideShows();
void removeSlidesFromAllCustomSlideShows();
};
#endif // TESTCUSTOMSLIDESHOWS_H
diff --git a/stage/part/tests/TestDelCustomSlideShowCommand.cpp b/stage/part/tests/TestDelCustomSlideShowCommand.cpp
index d0408710750..975cb6a7d04 100644
--- a/stage/part/tests/TestDelCustomSlideShowCommand.cpp
+++ b/stage/part/tests/TestDelCustomSlideShowCommand.cpp
@@ -1,77 +1,78 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestDelCustomSlideShowCommand.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include "commands/KPrDelCustomSlideShowCommand.h"
#include "KPrCustomSlideShows.h"
#include "KPrCustomSlideShowsModel.h"
-#include <qtest_kde.h>
+
+#include <QTest>
void TestDelCustomSlideShowCommand::delCustomSlideShow()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
QList<KoPAPageBase*> slideList;
slideList.append(page1);
slideList.append(page2);
slideList.append(page3);
QString customShowName = "test 1";
KPrCustomSlideShowsModel model(&doc, 0);
doc.customSlideShows()->insert(customShowName, slideList);
QCOMPARE(doc.customSlideShows()->names().count(), 1);
KPrDelCustomSlideShowCommand cmd(&doc, &model, customShowName);
cmd.redo();
QCOMPARE(doc.customSlideShows()->names().count(), 0);
cmd.undo();
QCOMPARE(doc.customSlideShows()->names().count(), 1);
}
-QTEST_KDEMAIN(TestDelCustomSlideShowCommand, GUI)
+QTEST_MAIN(TestDelCustomSlideShowCommand)
diff --git a/stage/part/tests/TestDelCustomSlideShowCommand.h b/stage/part/tests/TestDelCustomSlideShowCommand.h
index 09f29841031..20758c1ec72 100644
--- a/stage/part/tests/TestDelCustomSlideShowCommand.h
+++ b/stage/part/tests/TestDelCustomSlideShowCommand.h
@@ -1,32 +1,32 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTDELCUSTOMSLIDESHOWCOMMAND_H
#define TESTDELCUSTOMSLIDESHOWCOMMAND_H
-#include <QtTest>
+#include <QObject>
class TestDelCustomSlideShowCommand: public QObject
{
Q_OBJECT
private Q_SLOTS:
void delCustomSlideShow();
};
#endif // TESTDELCUSTOMSLIDESHOWCOMMAND_H
diff --git a/stage/part/tests/TestDeleteSlidesCommand.cpp b/stage/part/tests/TestDeleteSlidesCommand.cpp
index 9db9019dd50..4aea44ba22c 100644
--- a/stage/part/tests/TestDeleteSlidesCommand.cpp
+++ b/stage/part/tests/TestDeleteSlidesCommand.cpp
@@ -1,92 +1,93 @@
#include "TestDeleteSlidesCommand.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include "commands/KPrDeleteSlidesCommand.h"
#include "KPrCustomSlideShows.h"
-#include <qtest_kde.h>
+
+#include <QTest>
void TestDeleteSlidesCommand::delSlide()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
KoPAPage *page4 = new KoPAPage(master1);
doc.insertPage(page4, 0);
KoPAPage *page5 = new KoPAPage(master1);
doc.insertPage(page5, 0);
//Create List 1 2 4 1 5 2 2 3
QList<KoPAPageBase*> slideList1;
slideList1.append(page1);
slideList1.append(page2);
slideList1.append(page4);
slideList1.append(page1);
slideList1.append(page5);
slideList1.append(page2);
slideList1.append(page2);
slideList1.append(page3);
//Create List 1 2 3 4 5 4 3 2
QList<KoPAPageBase*> slideList2;
slideList2.append(page1);
slideList2.append(page2);
slideList2.append(page3);
slideList2.append(page4);
slideList2.append(page5);
slideList2.append(page4);
slideList2.append(page3);
slideList2.append(page2);
//expected Lists
QList<KoPAPageBase*> resultSlideList1(slideList1);
resultSlideList1.removeAll(page2);
QList<KoPAPageBase*> resultSlideList2(slideList2);
resultSlideList2.removeAll(page2);
QString customShowName1 = "test 1";
QString customShowName2 = "test 2";
doc.customSlideShows()->insert(customShowName1, slideList1);
doc.customSlideShows()->insert(customShowName2, slideList2);
KPrDeleteSlidesCommand cmd(&doc, page2);
cmd.redo();
//Page removed from document
QVERIFY(!doc.pages(false).contains(page2));
//Page removed from custom slide shows
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), resultSlideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), resultSlideList2);
cmd.undo();
QVERIFY(doc.pages(false).contains(page2));
QCOMPARE(doc.customSlideShows()->getByName(customShowName1), slideList1);
QCOMPARE(doc.customSlideShows()->getByName(customShowName2), slideList2);
}
-QTEST_KDEMAIN(TestDeleteSlidesCommand, GUI)
+QTEST_MAIN(TestDeleteSlidesCommand)
diff --git a/stage/part/tests/TestDeleteSlidesCommand.h b/stage/part/tests/TestDeleteSlidesCommand.h
index 3af609eb2e1..912f1dd63b4 100644
--- a/stage/part/tests/TestDeleteSlidesCommand.h
+++ b/stage/part/tests/TestDeleteSlidesCommand.h
@@ -1,13 +1,13 @@
#ifndef TESTDELETESLIDESCOMMAND_H
#define TESTDELETESLIDESCOMMAND_H
-#include <QtTest>
+#include <QObject>
class TestDeleteSlidesCommand: public QObject
{
Q_OBJECT
private Q_SLOTS:
void delSlide();
};
#endif // TESTDELETESLIDESCOMMAND_H
diff --git a/stage/part/tests/TestEditCustomSlideShowsCommand.cpp b/stage/part/tests/TestEditCustomSlideShowsCommand.cpp
index 46cae7911ba..812551f2e3f 100644
--- a/stage/part/tests/TestEditCustomSlideShowsCommand.cpp
+++ b/stage/part/tests/TestEditCustomSlideShowsCommand.cpp
@@ -1,82 +1,83 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestEditCustomSlideShowsCommand.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include "commands/KPrEditCustomSlideShowsCommand.h"
#include "KPrCustomSlideShows.h"
#include "KPrCustomSlideShowsModel.h"
-#include <qtest_kde.h>
+
+#include <QTest>
void TestEditCustomSlideShowsCommand::moveSingleSlide()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
QList<KoPAPageBase*> slideList;
slideList.append(page1);
slideList.append(page2);
slideList.append(page3);
QString customShowName = "test 1";
doc.customSlideShows()->insert(customShowName, slideList);
QList<KoPAPageBase*> initialSlideShow = doc.customSlideShows()->getByName(customShowName);
QCOMPARE(initialSlideShow.count(), 3);
initialSlideShow.move(0, 2);
KPrEditCustomSlideShowsCommand command(&doc, customShowName, initialSlideShow);
command.redo();
QList<KoPAPageBase*> modifiedSlideShow = doc.customSlideShows()->getByName(customShowName);
QCOMPARE(modifiedSlideShow, initialSlideShow);
command.undo();
modifiedSlideShow = doc.customSlideShows()->getByName(customShowName);
QCOMPARE(modifiedSlideShow.at(0), initialSlideShow.at(2));
QCOMPARE(modifiedSlideShow.at(1), initialSlideShow.at(0));
QCOMPARE(modifiedSlideShow.at(2), initialSlideShow.at(1));
}
-QTEST_KDEMAIN(TestEditCustomSlideShowsCommand, GUI)
+QTEST_MAIN(TestEditCustomSlideShowsCommand)
diff --git a/stage/part/tests/TestEditCustomSlideShowsCommand.h b/stage/part/tests/TestEditCustomSlideShowsCommand.h
index e6e7495ad0f..a3980bec7b5 100644
--- a/stage/part/tests/TestEditCustomSlideShowsCommand.h
+++ b/stage/part/tests/TestEditCustomSlideShowsCommand.h
@@ -1,35 +1,35 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTEDITCUSTOMSLIDESHOWSCOMMAND_H
#define TESTEDITCUSTOMSLIDESHOWSCOMMAND_H
-#include <QtTest>
+#include <QObject>
class TestEditCustomSlideShowsCommand : public QObject
{
Q_OBJECT
private Q_SLOTS:
//Just test one edit action because the command stores old and new list in the same
//way for all actions.
void moveSingleSlide();
};
#endif // TESTEDITCUSTOMSLIDESHOWSCOMMAND_H
diff --git a/stage/part/tests/TestRenameCustomSlideShowCommand.cpp b/stage/part/tests/TestRenameCustomSlideShowCommand.cpp
index da95ed60776..7146c73d37a 100644
--- a/stage/part/tests/TestRenameCustomSlideShowCommand.cpp
+++ b/stage/part/tests/TestRenameCustomSlideShowCommand.cpp
@@ -1,79 +1,80 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestRenameCustomSlideShowCommand.h"
#include "KPrDocument.h"
#include "KoPAMasterPage.h"
#include "KoPAPage.h"
#include "PAMock.h"
#include "commands/KPrRenameCustomSlideShowCommand.h"
#include "KPrCustomSlideShows.h"
#include "KPrCustomSlideShowsModel.h"
-#include <qtest_kde.h>
+
+#include <QTest>
void TestRenameCustomSlideShowCommand::renameCustomShow()
{
MockDocument doc;
KoPAMasterPage *master1 = new KoPAMasterPage();
doc.insertPage(master1, 0);
KoPAPage *page1 = new KoPAPage(master1);
doc.insertPage(page1, 0);
KoPAPage *p1 = dynamic_cast<KoPAPage *>(doc.pageByIndex(0, false));
KoPAMasterPage * m1 = dynamic_cast<KoPAMasterPage *>(doc.pageByIndex(0, true));
QVERIFY(p1 != 0);
QVERIFY(m1 != 0);
KoPAPage *page2 = new KoPAPage(master1);
doc.insertPage(page2, 0);
KoPAPage *page3 = new KoPAPage(master1);
doc.insertPage(page3, 0);
QList<KoPAPageBase*> slideList;
slideList.append(page1);
slideList.append(page2);
slideList.append(page3);
QString customShowName = "test 1";
QString customShowNewName = "new test 1";
KPrCustomSlideShowsModel model(&doc, 0);
doc.customSlideShows()->insert(customShowName, slideList);
QCOMPARE(doc.customSlideShows()->names().count(), 1);
KPrRenameCustomSlideShowCommand cmd(&doc, &model, customShowName, customShowNewName);
cmd.redo();
QVERIFY(!doc.customSlideShows()->names().contains(customShowName));
QVERIFY(doc.customSlideShows()->names().contains(customShowNewName));
cmd.undo();
QVERIFY(doc.customSlideShows()->names().contains(customShowName));
QVERIFY(!doc.customSlideShows()->names().contains(customShowNewName));
}
-QTEST_KDEMAIN(TestRenameCustomSlideShowCommand, GUI)
+QTEST_MAIN(TestRenameCustomSlideShowCommand)
diff --git a/stage/part/tests/TestRenameCustomSlideShowCommand.h b/stage/part/tests/TestRenameCustomSlideShowCommand.h
index 6cd3f21e28a..3595a5f6506 100644
--- a/stage/part/tests/TestRenameCustomSlideShowCommand.h
+++ b/stage/part/tests/TestRenameCustomSlideShowCommand.h
@@ -1,33 +1,33 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTRENAMECUSTOMSLIDESHOWCOMMAND_H
#define TESTRENAMECUSTOMSLIDESHOWCOMMAND_H
-#include <QtTest>
+#include <QObject>
class TestRenameCustomSlideShowCommand : public QObject
{
Q_OBJECT
private Q_SLOTS:
void renameCustomShow();
};
#endif // TESTRENAMECUSTOMSLIDESHOWCOMMAND_H
diff --git a/stage/part/tests/TestShapeAnimations.cpp b/stage/part/tests/TestShapeAnimations.cpp
index c55e5f896c3..e3b397ae280 100644
--- a/stage/part/tests/TestShapeAnimations.cpp
+++ b/stage/part/tests/TestShapeAnimations.cpp
@@ -1,525 +1,527 @@
/* This file is part of the KDE project
* Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org>
* Copyright ( C ) 2012 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or ( at your option ) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "TestShapeAnimations.h"
#include "MockAnimation.h"
#include <KPrShapeAnimations.h>
#include <MockShapes.h>
#include "../animations/KPrShapeAnimation.h"
#include "../animations/KPrAnimationStep.h"
#include "../animations/KPrAnimationSubStep.h"
#include "ModelTest.h"
#include "PAMock.h"
#include "MockShapeAnimation.h"
+#include <QTest>
+
const int ANIMATIONS_COUNT = 9;
void TestShapeAnimations::initTestCase()
{
//Initialize Animations
QTextBlockUserData *textBlockUserData = 0;
for (int i = 0; i < 9; i++) {
MockShape *shape = new MockShape();
shape->setSize(QSizeF(100, 100));
shapes.append(shape);
MockShapeAnimation *animation = new MockShapeAnimation(shape, textBlockUserData);
animation->setPresetClass(KPrShapeAnimation::Entrance);
m_animation.append(animation);
}
}
void TestShapeAnimations::addRemove()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
cleanStepSubStepData();
animations.add(m_animation[1]);
animations.add(m_animation[2]);
animations.add(m_animation[3]);
animations.add(m_animation[4]);
animations.add(m_animation[6]);
// Test animationByRowOutPut
QCOMPARE (animations.animationByRow(0), m_animation[1]);
QCOMPARE (animations.animationByRow(1), m_animation[2]);
QCOMPARE (animations.animationByRow(2), m_animation[3]);
QCOMPARE (animations.animationByRow(3), m_animation[4]);
QCOMPARE (animations.animationByRow(4), m_animation[6]);
//Test Order is updated
animations.remove(m_animation[1]);
animations.remove(m_animation[3]);
QCOMPARE (animations.animationByRow(0), m_animation[2]);
QCOMPARE (animations.animationByRow(1), m_animation[4]);
QCOMPARE (animations.animationByRow(2), m_animation[6]);
QVERIFY(animations.rowCount() == 3);
//Remove all animations
animations.remove(m_animation[2]);
animations.remove(m_animation[4]);
animations.remove(m_animation[6]);
QVERIFY(animations.rowCount() == 0);
}
void TestShapeAnimations::replaceSwap()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
cleanStepSubStepData();
animations.add(m_animation[1]);
animations.add(m_animation[2]);
animations.add(m_animation[3]);
//Test swap animations
animations.swapAnimations(m_animation[1], m_animation[3]);
QCOMPARE (animations.animationByRow(0), m_animation[3]);
QCOMPARE (animations.animationByRow(2), m_animation[1]);
QVERIFY(animations.rowCount() == 3);
//Test replace animation
animations.replaceAnimation(m_animation[2], m_animation[5]);
QCOMPARE(animations.animationByRow(1), m_animation[5]);
QCOMPARE(m_animation[2]->step(), m_animation[5]->step());
QCOMPARE(m_animation[2]->subStep(), m_animation[5]->subStep());
QVERIFY(animations.rowCount() == 3);
}
void TestShapeAnimations::helperMethods()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
cleanStepSubStepData();
animations.add(m_animation[1]);
animations.add(m_animation[2]);
animations.add(m_animation[4]);
//Test shapeByIndex
QCOMPARE(animations.shapeByIndex(animations.index(0, 0)), m_animation[1]->shape());
QCOMPARE(animations.shapeByIndex(animations.index(1, 0)), m_animation[2]->shape());
QCOMPARE(animations.shapeByIndex(animations.index(2, 0)), m_animation[4]->shape());
//Test indexByShape
QVERIFY(animations.indexByShape(m_animation[1]->shape()).isValid());
QCOMPARE(animations.index(0, 0), animations.indexByShape(m_animation[1]->shape()));
QVERIFY(animations.indexByShape(m_animation[2]->shape()).isValid());
QCOMPARE(animations.index(1, 0), animations.indexByShape(m_animation[2]->shape()));
QVERIFY(animations.indexByShape(m_animation[4]->shape()).isValid());
QCOMPARE(animations.index(2, 0), animations.indexByShape(m_animation[4]->shape()));
//Test indexByAnimation
QVERIFY(animations.indexByAnimation(m_animation[1]).isValid());
QCOMPARE(animations.index(0, 0), animations.indexByAnimation(m_animation[1]));
QVERIFY(animations.indexByAnimation(m_animation[2]).isValid());
QCOMPARE(animations.index(1, 0), animations.indexByAnimation(m_animation[2]));
QVERIFY(animations.indexByAnimation(m_animation[4]).isValid());
QCOMPARE(animations.index(2, 0), animations.indexByAnimation(m_animation[4]));
}
void TestShapeAnimations::getTriggerEvent()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
createAnimationTree(&animations);
// Test Trigger Event
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
// Test group
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::Group)).toInt(), 1);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::Group)).toInt(), 1);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::Group)).toInt(), 1);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::Group)).toInt(), 1);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::Group)).toInt(), 1);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::Group)).toInt(), 2);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::Group)).toInt(), 2);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::Group)).toInt(), 3);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::Group)).toInt(), 3);
checkOrder(&animations);
}
void TestShapeAnimations::setTriggerEvent()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
createAnimationTree(&animations);
// From On click
// To After Previous
animations.setNodeType(m_animation[5], KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// To With Previous
animations.setNodeType(m_animation[7], KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// From After Previous
// To On click
animations.setNodeType(m_animation[3], KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// To With previous
animations.setNodeType(m_animation[6], KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// From with previous
// To On click
animations.setNodeType(m_animation[1], KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// To after previous
animations.setNodeType(m_animation[6], KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
// From On click
// To After Previous (with childrem)
animations.setNodeType(m_animation[3], KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
//From On Click
// To after previous (invald for the first animation)
animations.setNodeType(m_animation[0], KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
//To with previous (invalid for the first item)
animations.setNodeType(m_animation[0], KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(0, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(1, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::OnClick);
QCOMPARE(animations.data(animations.index(2, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(3, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(4, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(5, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(6, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::AfterPrevious);
QCOMPARE(animations.data(animations.index(7, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
QCOMPARE(animations.data(animations.index(8, KPrShapeAnimations::NodeType)).toInt(),
(int)KPrShapeAnimation::WithPrevious);
checkOrder(&animations);
QVERIFY(animations.rowCount() == ANIMATIONS_COUNT);
}
void TestShapeAnimations::timeHelperMethods()
{
MockDocument doc;
KPrShapeAnimations animations(&doc);
new ModelTest(&animations, this);
createAnimationTree(&animations);
//Previous animation Begin
QCOMPARE(animations.animationStart(animations.index(0, 0)), 0);
QCOMPARE(animations.animationStart(animations.index(1, 0)), 0);
QCOMPARE(animations.animationStart(animations.index(2, 0)), 0);
QCOMPARE(animations.animationStart(animations.index(3, 0)), 5000);
QCOMPARE(animations.animationStart(animations.index(4, 0)), 5000);
QCOMPARE(animations.animationStart(animations.index(5, 0)), 1000);
QCOMPARE(animations.animationStart(animations.index(6, 0)), 3000);
QCOMPARE(animations.animationStart(animations.index(7, 0)), 0);
QCOMPARE(animations.animationStart(animations.index(8, 0)), 0);
//Previous animation End
QCOMPARE(animations.animationEnd(animations.index(0, 0)), 4000);
QCOMPARE(animations.animationEnd(animations.index(1, 0)), 2000);
QCOMPARE(animations.animationEnd(animations.index(2, 0)), 5000);
QCOMPARE(animations.animationEnd(animations.index(3, 0)), 6000);
QCOMPARE(animations.animationEnd(animations.index(4, 0)), 6000);
QCOMPARE(animations.animationEnd(animations.index(5, 0)), 3000);
QCOMPARE(animations.animationEnd(animations.index(6, 0)), 6000);
QCOMPARE(animations.animationEnd(animations.index(7, 0)), 5000);
QCOMPARE(animations.animationEnd(animations.index(8, 0)), 6000);
}
void TestShapeAnimations::cleanupTestCase()
{
qDeleteAll(shapes);
qDeleteAll(m_animation);
}
/* Tree structure:
Step 1
|_ SubStep1 1 2 3 4 5 6
|_Anim0 On click HHHHHHHHHHHHH
|_Anim1 With Previous HHHHH
|_Anim2 With Previous HHHHHHHHHHHHH
|_ SubStep2
|_Anim3 After Previous HHHHHH
|_Anim4 With Previous HHHHHH
Step 2
|_ SubStep3
|_Anim5 On click HHHHHHHHH
|_ SubStep4
|_Anim6 After Previous HHHHH
Step 3
|_ SubStep5
|_Anim7 On click HHHHHHHHHHHHHHHHH
|_Anim8 With Previous HHHHHHHHHHHH
*/
void TestShapeAnimations::createAnimationTree(KPrShapeAnimations *animations)
{
KPrAnimationStep *step1 = new KPrAnimationStep();
KPrAnimationStep *step2 = new KPrAnimationStep();
KPrAnimationStep *step3 = new KPrAnimationStep();
KPrAnimationSubStep *subStep1 = new KPrAnimationSubStep();
KPrAnimationSubStep *subStep2 = new KPrAnimationSubStep();
KPrAnimationSubStep *subStep3 = new KPrAnimationSubStep();
KPrAnimationSubStep *subStep4 = new KPrAnimationSubStep();
KPrAnimationSubStep *subStep5 = new KPrAnimationSubStep();
step1->addAnimation(subStep1);
step1->addAnimation(subStep2);
step2->addAnimation(subStep3);
step2->addAnimation(subStep4);
step3->addAnimation(subStep5);
subStep1->addAnimation(m_animation[0]);
subStep1->addAnimation(m_animation[1]);
subStep1->addAnimation(m_animation[2]);
subStep2->addAnimation(m_animation[3]);
subStep2->addAnimation(m_animation[4]);
subStep3->addAnimation(m_animation[5]);
subStep4->addAnimation(m_animation[6]);
subStep5->addAnimation(m_animation[7]);
subStep5->addAnimation(m_animation[8]);
foreach(MockShapeAnimation *animation, m_animation) {
animation->setBeginTime(0);
animation->setGlobalDuration(1);
}
//Set times
m_animation[0]->setGlobalDuration(4000);
m_animation[1]->setGlobalDuration(2000);
m_animation[2]->setBeginTime(2000);
m_animation[2]->setGlobalDuration(3000);
m_animation[3]->setGlobalDuration(1000);
m_animation[4]->setGlobalDuration(1000);
m_animation[5]->setBeginTime(1000);
m_animation[5]->setGlobalDuration(2000);
m_animation[6]->setBeginTime(2000);
m_animation[6]->setGlobalDuration(1000);
m_animation[7]->setGlobalDuration(5000);
m_animation[8]->setBeginTime(4000);
m_animation[8]->setGlobalDuration(2000);
QList<KPrAnimationStep *> stepsList;
stepsList.append(step1);
stepsList.append(step2);
stepsList.append(step3);
animations->init(stepsList);
animations->resyncStepsWithAnimations();
}
void TestShapeAnimations::cleanStepSubStepData()
{
foreach(MockShapeAnimation *animation, m_animation) {
animation->setStep(0);
animation->setSubStep(0);
}
}
void TestShapeAnimations::checkOrder(KPrShapeAnimations *animations)
{
for (int i = 0; i < m_animation.count(); i++) {
QCOMPARE (animations->animationByRow(i), m_animation[i]);
}
}
QTEST_MAIN(TestShapeAnimations)
diff --git a/stage/part/tests/TestShapeAnimations.h b/stage/part/tests/TestShapeAnimations.h
index f5a196b3d35..13c6c707fe9 100644
--- a/stage/part/tests/TestShapeAnimations.h
+++ b/stage/part/tests/TestShapeAnimations.h
@@ -1,52 +1,52 @@
/* This file is part of the KDE project
* Copyright ( C ) 2007 Thorsten Zachmann <zachmann@kde.org>
* Copyright ( C ) 2012 Paul Mendez <paulestebanms@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or ( at your option ) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef TESTSHAPEANIMATIONS_H
#define TESTSHAPEANIMATIONS_H
-#include <QtTest>
+#include <QObject>
class MockShapeAnimation;
class KPrShapeAnimations;
class MockShape;
class TestShapeAnimations : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void addRemove();
void replaceSwap();
void helperMethods();
void getTriggerEvent();
void setTriggerEvent();
void timeHelperMethods();
void cleanupTestCase();
private:
void createAnimationTree(KPrShapeAnimations *animations);
void cleanStepSubStepData();
void checkOrder(KPrShapeAnimations *animations);
QList<MockShapeAnimation *> m_animation;
QList<MockShape *> shapes;
};
#endif // TESTSHAPEANIMATIONS_H